From 74f65ed31560cfb7f024e2c242c30702d93eeb2f Mon Sep 17 00:00:00 2001 From: chelseahao Date: Mon, 18 Mar 2024 15:58:06 +0800 Subject: [PATCH 1/6] Remove flag, use new api for auto on in settings. Test: atest com.android.settings.bluetooth Bug: b/316822488 b/316985153 Change-Id: I8bc7651fd6e33175a3f95cce050b9a957ac6f241 --- .../BluetoothAutoOnPreferenceController.java | 116 +++++++++--------- .../BluetoothSwitchPreferenceController.java | 23 ++-- ...uetoothAutoOnPreferenceControllerTest.java | 38 ++---- 3 files changed, 83 insertions(+), 94 deletions(-) diff --git a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java index f6f62e8ffd8..3aca85e7118 100644 --- a/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceController.java @@ -16,11 +16,8 @@ package com.android.settings.bluetooth; +import android.bluetooth.BluetoothAdapter; import android.content.Context; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.UserHandle; -import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; @@ -30,39 +27,20 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.settings.core.TogglePreferenceController; +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.flags.Flags; import com.android.settingslib.utils.ThreadUtils; public class BluetoothAutoOnPreferenceController extends TogglePreferenceController - implements LifecycleObserver, OnStart, OnStop { - private static final String TAG = "BluetoothAutoOnPreferenceController"; + implements BluetoothCallback, LifecycleObserver, OnStart, OnStop { + private static final String TAG = "BluetoothAutoOnPrefCtlr"; @VisibleForTesting static final String PREF_KEY = "bluetooth_auto_on_settings_toggle"; - static final String SETTING_NAME = "bluetooth_automatic_turn_on"; - static final int UNSET = -1; - @VisibleForTesting static final int ENABLED = 1; - @VisibleForTesting static final int DISABLED = 0; - private final ContentObserver mContentObserver = - new ContentObserver(new Handler(/* async= */ true)) { - @Override - public void onChange(boolean selfChange) { - var unused = - ThreadUtils.postOnBackgroundThread( - () -> { - updateValue(); - mContext.getMainExecutor() - .execute( - () -> { - if (mPreference != null) { - updateState(mPreference); - } - }); - }); - } - }; - private int mAutoOnValue = UNSET; + @VisibleForTesting BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + private final LocalBluetoothManager mLocalBluetoothManager = Utils.getLocalBtManager(mContext); + private boolean mAutoOnValue = false; @Nullable private TwoStatePreference mPreference; public BluetoothAutoOnPreferenceController( @@ -70,27 +48,55 @@ public class BluetoothAutoOnPreferenceController extends TogglePreferenceControl super(context, preferenceKey); } + @Override + public void onAutoOnStateChanged(int state) { + var unused = + ThreadUtils.postOnBackgroundThread( + () -> { + Log.i(TAG, "onAutoOnStateChanged() state: " + state); + updateValue(); + mContext.getMainExecutor() + .execute( + () -> { + if (mPreference != null) { + updateState(mPreference); + } + }); + }); + } + @Override public void onStart() { - mContext.getContentResolver() - .registerContentObserver( - Settings.Secure.getUriFor(SETTING_NAME), - /* notifyForDescendants= */ false, - mContentObserver); + if (mLocalBluetoothManager == null) { + return; + } + mLocalBluetoothManager.getEventManager().registerCallback(this); } @Override public void onStop() { - mContext.getContentResolver().unregisterContentObserver(mContentObserver); + if (mLocalBluetoothManager == null) { + return; + } + mLocalBluetoothManager.getEventManager().unregisterCallback(this); } @Override public int getAvailabilityStatus() { - if (!Flags.bluetoothQsTileDialogAutoOnToggle()) { + if (mBluetoothAdapter == null) { + return UNSUPPORTED_ON_DEVICE; + } + try { + boolean isSupported = mBluetoothAdapter.isAutoOnSupported(); + Log.i(TAG, "getAvailabilityStatus() isSupported: " + isSupported); + if (isSupported) { + var unused = ThreadUtils.postOnBackgroundThread(this::updateValue); + } + return isSupported ? AVAILABLE : UNSUPPORTED_ON_DEVICE; + } catch (Exception e) { + // Server could throw TimeoutException, InterruptedException or ExecutionException return UNSUPPORTED_ON_DEVICE; } - updateValue(); - return mAutoOnValue != UNSET ? AVAILABLE : UNSUPPORTED_ON_DEVICE; } @Override @@ -106,26 +112,20 @@ public class BluetoothAutoOnPreferenceController extends TogglePreferenceControl @Override public boolean isChecked() { - return mAutoOnValue == ENABLED; + return mAutoOnValue; } @Override public boolean setChecked(boolean isChecked) { - if (getAvailabilityStatus() != AVAILABLE) { - Log.w(TAG, "Trying to set toggle value while feature not available."); - return false; - } var unused = ThreadUtils.postOnBackgroundThread( () -> { - boolean updated = - Settings.Secure.putIntForUser( - mContext.getContentResolver(), - SETTING_NAME, - isChecked ? ENABLED : DISABLED, - UserHandle.myUserId()); - if (updated) { - updateValue(); + try { + mBluetoothAdapter.setAutoOnEnabled(isChecked); + } catch (Exception e) { + // Server could throw IllegalStateException, TimeoutException, + // InterruptedException or ExecutionException + Log.e(TAG, "Error calling setAutoOnEnabled()", e); } }); return true; @@ -137,8 +137,14 @@ public class BluetoothAutoOnPreferenceController extends TogglePreferenceControl } private void updateValue() { - mAutoOnValue = - Settings.Secure.getIntForUser( - mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId()); + if (mBluetoothAdapter == null) { + return; + } + try { + mAutoOnValue = mBluetoothAdapter.isAutoOnEnabled(); + } catch (Exception e) { + // Server could throw TimeoutException, InterruptedException or ExecutionException + Log.e(TAG, "Error calling isAutoOnEnabled()", e); + } } } diff --git a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java index ac5575803c1..fc8365d1777 100644 --- a/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java +++ b/src/com/android/settings/bluetooth/BluetoothSwitchPreferenceController.java @@ -15,13 +15,10 @@ */ package com.android.settings.bluetooth; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET; - import android.app.settings.SettingsEnums; +import android.bluetooth.BluetoothAdapter; import android.content.Context; -import android.os.UserHandle; -import android.provider.Settings; +import android.util.Log; import android.view.View; import androidx.annotation.VisibleForTesting; @@ -34,7 +31,6 @@ import com.android.settings.widget.SwitchWidgetController; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.flags.Flags; import com.android.settingslib.widget.FooterPreference; /** @@ -47,11 +43,13 @@ public class BluetoothSwitchPreferenceController OnStop, SwitchWidgetController.OnSwitchChangeListener, View.OnClickListener { + private static final String TAG = "BluetoothSwitchPrefCtrl"; private BluetoothEnabler mBluetoothEnabler; private RestrictionUtils mRestrictionUtils; private SwitchWidgetController mSwitch; private Context mContext; + private BluetoothAdapter mBluetoothAdapter; private FooterPreference mFooterPreference; private boolean mIsAlwaysDiscoverable; @@ -87,6 +85,7 @@ public class BluetoothSwitchPreferenceController mRestrictionUtils); mBluetoothEnabler.setToggleCallback(this); mAlwaysDiscoverable = new AlwaysDiscoverable(context); + mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); } @Override @@ -157,11 +156,15 @@ public class BluetoothSwitchPreferenceController } private boolean isAutoOnFeatureAvailable() { - if (!Flags.bluetoothQsTileDialogAutoOnToggle()) { + if (mBluetoothAdapter == null) { + return false; + } + try { + return mBluetoothAdapter.isAutoOnSupported(); + } catch (Exception e) { + // Server could throw TimeoutException, InterruptedException or ExecutionException + Log.e(TAG, "Error calling isAutoOnFeatureAvailable()", e); return false; } - return Settings.Secure.getIntForUser( - mContext.getContentResolver(), SETTING_NAME, UNSET, UserHandle.myUserId()) - != UNSET; } } diff --git a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java index d82dcc43ac3..3980711b864 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/BluetoothAutoOnPreferenceControllerTest.java @@ -16,82 +16,62 @@ package com.android.settings.bluetooth; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.DISABLED; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.ENABLED; import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.PREF_KEY; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.SETTING_NAME; -import static com.android.settings.bluetooth.BluetoothAutoOnPreferenceController.UNSET; import static com.android.settings.core.BasePreferenceController.AVAILABLE; import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; -import static com.android.settingslib.flags.Flags.FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import android.bluetooth.BluetoothAdapter; import android.content.ContentResolver; import android.content.Context; -import android.platform.test.flag.junit.SetFlagsRule; -import android.provider.Settings; import androidx.test.core.app.ApplicationProvider; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @RunWith(RobolectricTestRunner.class) public class BluetoothAutoOnPreferenceControllerTest { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private Context mContext; private ContentResolver mContentResolver; private BluetoothAutoOnPreferenceController mController; + private BluetoothAdapter mBluetoothAdapter; @Before public void setUp() { - mSetFlagsRule.enableFlags(FLAG_BLUETOOTH_QS_TILE_DIALOG_AUTO_ON_TOGGLE); mContext = spy(ApplicationProvider.getApplicationContext()); mContentResolver = mContext.getContentResolver(); mController = new BluetoothAutoOnPreferenceController(mContext, PREF_KEY); + mBluetoothAdapter = spy(BluetoothAdapter.getDefaultAdapter()); + mController.mBluetoothAdapter = mBluetoothAdapter; } @Test public void getAvailability_valueUnset_returnUnsupported() { - Settings.Secure.putInt(mContentResolver, SETTING_NAME, UNSET); + doReturn(false).when(mBluetoothAdapter).isAutoOnSupported(); assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); } @Test public void getAvailability_valueSet_returnAvailable() { - Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED); + doReturn(true).when(mBluetoothAdapter).isAutoOnSupported(); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } @Test public void isChecked_valueEnabled_returnTrue() { - Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED); + doReturn(true).when(mBluetoothAdapter).isAutoOnSupported(); + doReturn(true).when(mBluetoothAdapter).isAutoOnEnabled(); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); assertThat(mController.isChecked()).isEqualTo(true); } - - @Test - public void setChecked_returnTrue() { - Settings.Secure.putInt(mContentResolver, SETTING_NAME, DISABLED); - - mController.setChecked(true); - assertThat(mController.isChecked()).isEqualTo(true); - } - - @Test - public void setChecked_returnFalse() { - Settings.Secure.putInt(mContentResolver, SETTING_NAME, ENABLED); - - mController.setChecked(false); - assertThat(mController.isChecked()).isEqualTo(false); - } } From ad0b01e7cfff231d12d746d22f3861eae61b47e9 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 15 Mar 2024 16:43:25 +0800 Subject: [PATCH 2/6] Add BootBroadcastReceiver required permissions These permissions are required for any Settings tests to run, otherwise the tests will fails. Instead of adding these permissions to each of the tests, add it to the Settings-core. Bug: 329046490 Test: unit tests Change-Id: I54dc1ebfdd062acc224021429dce318d7c12809b --- AndroidManifest.xml | 3 +++ tests/spa_unit/AndroidManifest.xml | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 5ffbbf84d01..810538ec8e3 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -143,6 +143,9 @@ + + + - - - From 55899976e95d22d892eebffadd19dfbbe29e6490 Mon Sep 17 00:00:00 2001 From: tomhsu Date: Wed, 20 Mar 2024 08:29:43 +0000 Subject: [PATCH 3/6] Fix concurrent issue - Add a thread lock Fix: 286495550 Test: make pass Change-Id: I72877551bda18cf93734d270770a0bd53bab465b --- src/com/android/settings/network/MobileNetworkRepository.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/network/MobileNetworkRepository.java b/src/com/android/settings/network/MobileNetworkRepository.java index b0c85fcfdfc..381f3c1b58f 100644 --- a/src/com/android/settings/network/MobileNetworkRepository.java +++ b/src/com/android/settings/network/MobileNetworkRepository.java @@ -295,7 +295,9 @@ public class MobileNetworkRepository extends SubscriptionManager.OnSubscriptions } public void removeRegister(MobileNetworkCallback mobileNetworkCallback) { - sCallbacks.remove(mobileNetworkCallback); + synchronized (this) { + sCallbacks.remove(mobileNetworkCallback); + } if (sCallbacks.isEmpty()) { mSubscriptionManager.removeOnSubscriptionsChangedListener(this); mAirplaneModeObserver.unRegister(mContext); From 8ce68baa24f60cd21e9313da3cb461424534eaee Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Wed, 20 Mar 2024 13:42:57 +0800 Subject: [PATCH 4/6] Update percentage symbol from western sign to arabic sign (https://screenshot.googleplex.com/ALgEzpDKG4BMBB4) Fix: 322855881 Test: manual Change-Id: I1335996814a1b80006481f52201a107144497859 --- res/values/strings.xml | 3 --- .../fuelgauge/BatteryHeaderPreferenceController.java | 6 +----- .../fuelgauge/BatteryHeaderPreferenceControllerTest.java | 6 +----- 3 files changed, 2 insertions(+), 13 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index dd51f522b1f..66acfaa2853 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -5814,9 +5814,6 @@ other {# apps restricted} } - - ^1 % - Problem reading the battery meter. diff --git a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java index 6a65dc07c88..907c89faec0 100644 --- a/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java +++ b/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceController.java @@ -17,10 +17,8 @@ package com.android.settings.fuelgauge; import android.content.Context; import android.content.Intent; -import android.icu.text.NumberFormat; import android.os.BatteryManager; import android.os.PowerManager; -import android.text.TextUtils; import android.util.Log; import androidx.annotation.VisibleForTesting; @@ -145,8 +143,6 @@ public class BatteryHeaderPreferenceController extends BasePreferenceController } private CharSequence formatBatteryPercentageText(int batteryLevel) { - return TextUtils.expandTemplate( - mContext.getText(R.string.battery_header_title_alternate), - NumberFormat.getIntegerInstance().format(batteryLevel)); + return com.android.settings.Utils.formatPercentage(batteryLevel); } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java index 1df8a406b69..9457f99a42d 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryHeaderPreferenceControllerTest.java @@ -28,10 +28,8 @@ import android.content.Intent; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; -import android.icu.text.NumberFormat; import android.os.BatteryManager; import android.os.PowerManager; -import android.text.TextUtils; import androidx.preference.PreferenceScreen; @@ -320,9 +318,7 @@ public class BatteryHeaderPreferenceControllerTest { } private CharSequence formatBatteryPercentageText() { - return TextUtils.expandTemplate( - mContext.getText(R.string.battery_header_title_alternate), - NumberFormat.getIntegerInstance().format(BATTERY_LEVEL)); + return com.android.settings.Utils.formatPercentage(BATTERY_LEVEL); } private void setChargingState(boolean isDischarging, boolean updatedByStatusFeature) { From 9578b97212144710bd91a50f322b640435b1f11f Mon Sep 17 00:00:00 2001 From: Charlotte Lu Date: Thu, 21 Mar 2024 13:32:18 +0800 Subject: [PATCH 5/6] Add certificate summary. Test: Manual Fix: 330651393 Change-Id: I98ecf675edead58b5ffa525aa65aceabf1d701c4 --- res/values/strings.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index de733b8262f..46990faa94b 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2319,6 +2319,14 @@ Randomized MAC address (last used) + + + %d certificates + + System certificate + + Certificate pinning + Network details @@ -6263,6 +6271,10 @@ Installed for VPN and apps Installed for Wi\u2011Fi + + Installed for %s + + Using for %s Installed for Wi\u2011Fi (In use) From 54b03da369a474ff1811938bc2840529c6859649 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 21 Mar 2024 16:32:38 +0800 Subject: [PATCH 6/6] Fix readOnlyApnFields not applied Some enabled not set correctly. Remove the enabled values, and check readOnlyApnFields directly to fix. Fix: 330566517 Test: manual with carrier config KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY Test: unit test Change-Id: Ifd6d4a0d9a96a900c9d124c9e357562e84bbc6c7 --- .../network/apn/ApnEditPageProvider.kt | 34 ++++---- .../network/apn/ApnNetworkTypeCheckBox.kt | 7 +- .../android/settings/network/apn/ApnStatus.kt | 87 ++----------------- .../settings/network/apn/ApnTypeCheckBox.kt | 3 +- .../settings/network/apn/ApnStatusTest.kt | 42 +++++++-- 5 files changed, 68 insertions(+), 105 deletions(-) diff --git a/src/com/android/settings/network/apn/ApnEditPageProvider.kt b/src/com/android/settings/network/apn/ApnEditPageProvider.kt index 1abcbdb953c..a287b848474 100644 --- a/src/com/android/settings/network/apn/ApnEditPageProvider.kt +++ b/src/com/android/settings/network/apn/ApnEditPageProvider.kt @@ -18,6 +18,7 @@ package com.android.settings.network.apn import android.net.Uri import android.os.Bundle +import android.provider.Telephony import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -142,39 +143,39 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState, uriInit: Ur SettingsOutlinedTextField( value = apnData.name, label = stringResource(R.string.apn_name), - enabled = apnData.nameEnabled, + enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME), errorMessage = validateName(apnData.validEnabled, apnData.name, context) ) { apnData = apnData.copy(name = it) } SettingsOutlinedTextField( value = apnData.apn, label = stringResource(R.string.apn_apn), - enabled = apnData.apnEnabled, + enabled = apnData.isFieldEnabled(Telephony.Carriers.APN), errorMessage = validateAPN(apnData.validEnabled, apnData.apn, context) ) { apnData = apnData.copy(apn = it) } SettingsOutlinedTextField( value = apnData.proxy, label = stringResource(R.string.apn_http_proxy), - enabled = apnData.proxyEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.PROXY), ) { apnData = apnData.copy(proxy = it) } SettingsOutlinedTextField( value = apnData.port, label = stringResource(R.string.apn_http_port), - enabled = apnData.portEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.PORT), ) { apnData = apnData.copy(port = it) } SettingsOutlinedTextField( value = apnData.userName, label = stringResource(R.string.apn_user), - enabled = apnData.userNameEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.USER), ) { apnData = apnData.copy(userName = it) } SettingsTextFieldPassword( value = apnData.passWord, label = stringResource(R.string.apn_password), - enabled = apnData.passWordEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.PASSWORD), ) { apnData = apnData.copy(passWord = it) } SettingsOutlinedTextField( value = apnData.server, label = stringResource(R.string.apn_server), - enabled = apnData.serverEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.SERVER), ) { apnData = apnData.copy(server = it) } ApnTypeCheckBox( apnData = apnData, @@ -186,42 +187,45 @@ fun ApnPage(apnDataInit: ApnData, apnDataCur: MutableState, uriInit: Ur value = apnData.mmsc, label = stringResource(R.string.apn_mmsc), errorMessage = validateMMSC(apnData.validEnabled, apnData.mmsc, context), - enabled = apnData.mmscEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSC), ) { apnData = apnData.copy(mmsc = it) } SettingsOutlinedTextField( value = apnData.mmsProxy, label = stringResource(R.string.apn_mms_proxy), - enabled = apnData.mmsProxyEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSPROXY), ) { apnData = apnData.copy(mmsProxy = it) } SettingsOutlinedTextField( value = apnData.mmsPort, label = stringResource(R.string.apn_mms_port), - enabled = apnData.mmsPortEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.MMSPORT), ) { apnData = apnData.copy(mmsPort = it) } } SettingsDropdownBox( label = stringResource(R.string.apn_auth_type), options = authTypeOptions, selectedOptionIndex = apnData.authType, - enabled = apnData.authTypeEnabled, + enabled = apnData.isFieldEnabled(Telephony.Carriers.AUTH_TYPE), ) { apnData = apnData.copy(authType = it) } SettingsDropdownBox( label = stringResource(R.string.apn_protocol), options = apnProtocolOptions, selectedOptionIndex = apnData.apnProtocol, - enabled = apnData.apnProtocolEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.PROTOCOL), ) { apnData = apnData.copy(apnProtocol = it) } SettingsDropdownBox( label = stringResource(R.string.apn_roaming_protocol), options = apnProtocolOptions, selectedOptionIndex = apnData.apnRoaming, - enabled = apnData.apnRoamingEnabled + enabled = apnData.isFieldEnabled(Telephony.Carriers.ROAMING_PROTOCOL), ) { apnData = apnData.copy(apnRoaming = it) } ApnNetworkTypeCheckBox(apnData) { apnData = apnData.copy(networkType = it) } SwitchPreference( object : SwitchPreferenceModel { - override val title = context.resources.getString(R.string.carrier_enabled) - override val changeable = { apnData.apnEnableEnabled } + override val title = stringResource(R.string.carrier_enabled) + override val changeable = { + apnData.apnEnableEnabled && + apnData.isFieldEnabled(Telephony.Carriers.CARRIER_ENABLED) + } override val checked = { apnData.apnEnable } override val onCheckedChange = { newChecked: Boolean -> apnData = apnData.copy(apnEnable = newChecked) diff --git a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt index a42031b388a..305f7b6061a 100644 --- a/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt +++ b/src/com/android/settings/network/apn/ApnNetworkTypeCheckBox.kt @@ -16,6 +16,7 @@ package com.android.settings.network.apn +import android.provider.Telephony import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.res.stringResource @@ -29,7 +30,11 @@ fun ApnNetworkTypeCheckBox(apnData: ApnData, onNetworkTypeChanged: (Long) -> Uni label = stringResource(R.string.network_type), options = options, emptyText = stringResource(R.string.network_type_unspecified), - enabled = apnData.networkTypeEnabled, + enabled = apnData.isFieldEnabled( + Telephony.Carriers.BEARER, + Telephony.Carriers.BEARER_BITMASK, + Telephony.Carriers.NETWORK_TYPE_BITMASK + ), ) { onNetworkTypeChanged(ApnNetworkTypes.optionsToNetworkType(options)) } diff --git a/src/com/android/settings/network/apn/ApnStatus.kt b/src/com/android/settings/network/apn/ApnStatus.kt index ab16f1ca5d2..aa816fc8a62 100644 --- a/src/com/android/settings/network/apn/ApnStatus.kt +++ b/src/com/android/settings/network/apn/ApnStatus.kt @@ -48,22 +48,7 @@ data class ApnData( val networkType: Long = 0, val edited: Int = Telephony.Carriers.USER_EDITED, val userEditable: Int = 1, - val nameEnabled: Boolean = true, - val apnEnabled: Boolean = true, - val proxyEnabled: Boolean = true, - val portEnabled: Boolean = true, - val userNameEnabled: Boolean = true, - val passWordEnabled: Boolean = true, - val serverEnabled: Boolean = true, - val mmscEnabled: Boolean = true, - val mmsProxyEnabled: Boolean = true, - val mmsPortEnabled: Boolean = true, - val authTypeEnabled: Boolean = true, - val apnTypeEnabled: Boolean = true, - val apnProtocolEnabled: Boolean = true, - val apnRoamingEnabled: Boolean = true, val apnEnableEnabled: Boolean = true, - val networkTypeEnabled: Boolean = true, val newApn: Boolean = false, val subId: Int = -1, val validEnabled: Boolean = false, @@ -93,6 +78,10 @@ data class ApnData( if (newApn) context.getApnIdMap(subId).forEach(::putObject) getContentValueMap(context).forEach(::putObject) } + + fun isFieldEnabled(vararg fieldName: String): Boolean = + !customizedConfig.readOnlyApn && + fieldName.all { it !in customizedConfig.readOnlyApnFields } } data class CustomizedConfig( @@ -271,83 +260,17 @@ private fun ApnData.isReadOnly(): Boolean { fun disableInit(apnDataInit: ApnData): ApnData { if (apnDataInit.isReadOnly()) { Log.d(TAG, "disableInit: read-only APN") - val apnData = apnDataInit.copy( + return apnDataInit.copy( customizedConfig = apnDataInit.customizedConfig.copy(readOnlyApn = true) ) - return disableAllFields(apnData) } val readOnlyApnFields = apnDataInit.customizedConfig.readOnlyApnFields if (readOnlyApnFields.isNotEmpty()) { Log.d(TAG, "disableInit: readOnlyApnFields $readOnlyApnFields)") - return disableFields(readOnlyApnFields, apnDataInit) } return apnDataInit } -/** - * Disables all fields so that user cannot modify the APN - */ -private fun disableAllFields(apnDataInit: ApnData): ApnData { - var apnData = apnDataInit - apnData = apnData.copy(nameEnabled = false) - apnData = apnData.copy(apnEnabled = false) - apnData = apnData.copy(proxyEnabled = false) - apnData = apnData.copy(portEnabled = false) - apnData = apnData.copy(userNameEnabled = false) - apnData = apnData.copy(passWordEnabled = false) - apnData = apnData.copy(serverEnabled = false) - apnData = apnData.copy(mmscEnabled = false) - apnData = apnData.copy(mmsProxyEnabled = false) - apnData = apnData.copy(mmsPortEnabled = false) - apnData = apnData.copy(authTypeEnabled = false) - apnData = apnData.copy(apnTypeEnabled = false) - apnData = apnData.copy(apnProtocolEnabled = false) - apnData = apnData.copy(apnRoamingEnabled = false) - apnData = apnData.copy(apnEnableEnabled = false) - apnData = apnData.copy(networkTypeEnabled = false) - return apnData -} - -/** - * Disables given fields so that user cannot modify them - * - * @param apnFields fields to be disabled - */ -private fun disableFields(apnFields: List, apnDataInit: ApnData): ApnData { - var apnData = apnDataInit - for (apnField in apnFields) { - apnData = disableByFieldName(apnField, apnDataInit) - } - return apnData -} - -private fun disableByFieldName(apnField: String, apnDataInit: ApnData): ApnData { - var apnData = apnDataInit - when (apnField) { - Telephony.Carriers.NAME -> apnData = apnData.copy(nameEnabled = false) - Telephony.Carriers.APN -> apnData = apnData.copy(apnEnabled = false) - Telephony.Carriers.PROXY -> apnData = apnData.copy(proxyEnabled = false) - Telephony.Carriers.PORT -> apnData = apnData.copy(portEnabled = false) - Telephony.Carriers.USER -> apnData = apnData.copy(userNameEnabled = false) - Telephony.Carriers.SERVER -> apnData = apnData.copy(serverEnabled = false) - Telephony.Carriers.PASSWORD -> apnData = apnData.copy(passWordEnabled = false) - Telephony.Carriers.MMSPROXY -> apnData = apnData.copy(mmsProxyEnabled = false) - Telephony.Carriers.MMSPORT -> apnData = apnData.copy(mmsPortEnabled = false) - Telephony.Carriers.MMSC -> apnData = apnData.copy(mmscEnabled = false) - Telephony.Carriers.TYPE -> apnData = apnData.copy(apnTypeEnabled = false) - Telephony.Carriers.AUTH_TYPE -> apnData = apnData.copy(authTypeEnabled = false) - Telephony.Carriers.PROTOCOL -> apnData = apnData.copy(apnProtocolEnabled = false) - Telephony.Carriers.ROAMING_PROTOCOL -> apnData = apnData.copy(apnRoamingEnabled = false) - Telephony.Carriers.CARRIER_ENABLED -> apnData = apnData.copy(apnEnableEnabled = false) - Telephony.Carriers.BEARER, Telephony.Carriers.BEARER_BITMASK, - Telephony.Carriers.NETWORK_TYPE_BITMASK -> apnData = apnData.copy( - networkTypeEnabled = - false - ) - } - return apnData -} - fun deleteApn(uri: Uri, context: Context) { val contentResolver = context.contentResolver contentResolver.delete(uri, null, null) diff --git a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt index 4d0659c659c..aa757cc9513 100644 --- a/src/com/android/settings/network/apn/ApnTypeCheckBox.kt +++ b/src/com/android/settings/network/apn/ApnTypeCheckBox.kt @@ -16,6 +16,7 @@ package com.android.settings.network.apn +import android.provider.Telephony import android.telephony.data.ApnSetting import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -45,7 +46,7 @@ fun ApnTypeCheckBox( SettingsDropdownCheckBox( label = stringResource(R.string.apn_type), options = apnTypeOptions, - enabled = apnData.apnTypeEnabled, + enabled = apnData.isFieldEnabled(Telephony.Carriers.TYPE), ) { onTypeChanged(apnTypeOptions.toApnType()) updateMmsSelected() diff --git a/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt index 539783c18a3..6b61eba0f9b 100644 --- a/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/apn/ApnStatusTest.kt @@ -17,8 +17,10 @@ package com.android.settings.network.apn import android.os.PersistableBundle +import android.provider.Telephony import android.telephony.CarrierConfigManager import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn @@ -26,11 +28,8 @@ import org.mockito.kotlin.mock @RunWith(AndroidJUnit4::class) class ApnStatusTest { - private val apnData = mock { - on { - it.subId - } doReturn 1 - } + private val apnData = ApnData(subId = 1) + private val configManager = mock { val p = PersistableBundle() p.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, true) @@ -51,4 +50,35 @@ class ApnStatusTest { fun getCarrierCustomizedConfig_test() { assert(getCarrierCustomizedConfig(apnData, configManager).isAddApnAllowed) } -} \ No newline at end of file + + @Test + fun isFieldEnabled_default() { + val apnData = ApnData() + + val enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME) + + assertThat(enabled).isTrue() + } + + @Test + fun isFieldEnabled_readOnlyApn() { + val apnData = ApnData(customizedConfig = CustomizedConfig(readOnlyApn = true)) + + val enabled = apnData.isFieldEnabled(Telephony.Carriers.NAME) + + assertThat(enabled).isFalse() + } + + @Test + fun isFieldEnabled_readOnlyApnFields() { + val apnData = ApnData( + customizedConfig = CustomizedConfig( + readOnlyApnFields = listOf(Telephony.Carriers.NAME, Telephony.Carriers.PROXY), + ), + ) + + assertThat(apnData.isFieldEnabled(Telephony.Carriers.NAME)).isFalse() + assertThat(apnData.isFieldEnabled(Telephony.Carriers.PROXY)).isFalse() + assertThat(apnData.isFieldEnabled(Telephony.Carriers.APN)).isTrue() + } +}