From 2545f06558bebe579bce98b5e81139423d131d2f Mon Sep 17 00:00:00 2001 From: hoffc Date: Tue, 8 Oct 2024 17:29:32 +0800 Subject: [PATCH 1/7] Fix force close for updating UI after activity destroyed. If activity is finishing or destroyed, getting activity context will return null results in settings app force close. Directly return if network select activity is finishing or destroyed when handling network scan callback. Bug: 372123288 Change-Id: Ia8c89569761b66da58cafd0140ceda29ac7d678c --- .../settings/network/telephony/NetworkSelectSettings.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index a16f4b5d2d7..a3485cd6c3a 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -368,6 +368,11 @@ public class NetworkSelectSettings extends DashboardFragment { @VisibleForTesting protected void scanResultHandler(NetworkScanRepository.NetworkScanResult results) { + if (isFinishingOrDestroyed()) { + Log.d(TAG, "scanResultHandler: activity isFinishingOrDestroyed, directly return"); + return; + } + mCellInfoList = filterOutSatellitePlmn(results.getCellInfos()); Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); updateAllPreferenceCategory(); From c850dfac269918dafc2347b5e491c0d3954bb4e7 Mon Sep 17 00:00:00 2001 From: Riley Jones Date: Wed, 16 Oct 2024 18:58:35 +0000 Subject: [PATCH 2/7] Removes the A11y tutorial that displays on change to gesture navigation Switching to gesture navigation puts button mode in FAB, so there's no longer a need to show a gesture tutorial. Test: change navigation modes. Verify no tutorial is shown for either option Flag: android.provider.a11y_standalone_gesture_enabled Bug: 371027085 Change-Id: I71e33efea3e25d22b0bc41c33b17de11f9ef2d64 --- .../settings/gestures/SystemNavigationGestureSettings.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java index cfaee006d39..9b4da46a98d 100644 --- a/src/com/android/settings/gestures/SystemNavigationGestureSettings.java +++ b/src/com/android/settings/gestures/SystemNavigationGestureSettings.java @@ -243,7 +243,9 @@ public class SystemNavigationGestureSettings extends RadioButtonPickerFragment i protected boolean setDefaultKey(String key) { setCurrentSystemNavigationMode(mOverlayManager, key); setIllustrationVideo(mVideoPreference, key); - setGestureNavigationTutorialDialog(key); + if (!android.provider.Flags.a11yStandaloneGestureEnabled()) { + setGestureNavigationTutorialDialog(key); + } setIllustrationClickListener(mVideoPreference, key); return true; } From d0d3879fa4d88019339d4905c558527a33f4ac3c Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Fri, 18 Oct 2024 15:45:17 +0800 Subject: [PATCH 3/7] Redirect to new APN edit page Bug: 374226933 Flag: EXEMPT clean up Test: am start -a android.intent.action.INSERT -d content://telephony/carriers --ei sub_id 2 Test: am start -a android.intent.action.EDIT -d content://telephony/carriers/208 --ei sub_id 2 Change-Id: I1c5e3a271e1dc85930d73b5b56b3e655821c858b --- .../settings/network/apn/ApnEditor.java | 1425 +---------------- .../settings/network/apn/ApnEditorTest.java | 625 -------- 2 files changed, 24 insertions(+), 2026 deletions(-) delete mode 100644 tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java diff --git a/src/com/android/settings/network/apn/ApnEditor.java b/src/com/android/settings/network/apn/ApnEditor.java index dc9741d67bc..533fd292289 100644 --- a/src/com/android/settings/network/apn/ApnEditor.java +++ b/src/com/android/settings/network/apn/ApnEditor.java @@ -16,153 +16,33 @@ package com.android.settings.network.apn; -import android.app.Dialog; +import static com.android.settings.network.apn.ApnEditPageProviderKt.EDIT_URL; +import static com.android.settings.network.apn.ApnEditPageProviderKt.INSERT_URL; + import android.app.settings.SettingsEnums; -import android.content.ContentValues; -import android.content.Context; import android.content.Intent; -import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.PersistableBundle; import android.os.UserManager; import android.provider.Telephony; import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.text.TextUtils; import android.util.Log; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnKeyListener; -import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import androidx.appcompat.app.AlertDialog; -import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.MultiSelectListPreference; -import androidx.preference.Preference; -import androidx.preference.Preference.OnPreferenceChangeListener; -import androidx.preference.TwoStatePreference; import com.android.internal.util.ArrayUtils; -import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.core.instrumentation.InstrumentedDialogFragment; -import com.android.settings.network.ProxySubscriptionManager; -import com.android.settingslib.utils.ThreadUtils; +import com.android.settings.spa.SpaActivity; import java.util.Arrays; -import java.util.HashSet; import java.util.List; -import java.util.Objects; -import java.util.Set; /** Use to edit apn settings. */ -public class ApnEditor extends SettingsPreferenceFragment - implements OnPreferenceChangeListener, OnKeyListener { +public class ApnEditor extends SettingsPreferenceFragment { private static final String TAG = ApnEditor.class.getSimpleName(); - private static final boolean VDBG = false; // STOPSHIP if true - - private static final String KEY_AUTH_TYPE = "auth_type"; - private static final String KEY_APN_TYPE = "apn_type"; - private static final String KEY_PROTOCOL = "apn_protocol"; - private static final String KEY_ROAMING_PROTOCOL = "apn_roaming_protocol"; - private static final String KEY_CARRIER_ENABLED = "carrier_enabled"; - private static final String KEY_BEARER_MULTI = "bearer_multi"; - private static final String KEY_MVNO_TYPE = "mvno_type"; - private static final String KEY_PASSWORD = "apn_password"; - - @VisibleForTesting - static final int MENU_DELETE = Menu.FIRST; - private static final int MENU_SAVE = Menu.FIRST + 1; - private static final int MENU_CANCEL = Menu.FIRST + 2; - - @VisibleForTesting - static String sNotSet; - @VisibleForTesting - EditTextPreference mName; - @VisibleForTesting - EditTextPreference mApn; - @VisibleForTesting - EditTextPreference mProxy; - @VisibleForTesting - EditTextPreference mPort; - @VisibleForTesting - EditTextPreference mUser; - @VisibleForTesting - EditTextPreference mServer; - @VisibleForTesting - EditTextPreference mPassword; - @VisibleForTesting - EditTextPreference mMmsc; - @VisibleForTesting - EditTextPreference mMcc; - @VisibleForTesting - EditTextPreference mMnc; - @VisibleForTesting - EditTextPreference mMmsProxy; - @VisibleForTesting - EditTextPreference mMmsPort; - @VisibleForTesting - ListPreference mAuthType; - @VisibleForTesting - EditTextPreference mApnType; - @VisibleForTesting - ListPreference mProtocol; - @VisibleForTesting - ListPreference mRoamingProtocol; - @VisibleForTesting - TwoStatePreference mCarrierEnabled; - @VisibleForTesting - MultiSelectListPreference mBearerMulti; - @VisibleForTesting - ListPreference mMvnoType; - @VisibleForTesting - EditTextPreference mMvnoMatchData; - - @VisibleForTesting - ApnData mApnData; - - private String mCurMnc; - private String mCurMcc; - - private boolean mNewApn; - private int mSubId; - @VisibleForTesting - ProxySubscriptionManager mProxySubscriptionMgr; - private int mBearerInitialVal = 0; - private String mMvnoTypeStr; - private String mMvnoMatchDataStr; - @VisibleForTesting - String[] mReadOnlyApnTypes; - @VisibleForTesting - String[] mDefaultApnTypes; - @VisibleForTesting - String mDefaultApnProtocol; - @VisibleForTesting - String mDefaultApnRoamingProtocol; - private String[] mReadOnlyApnFields; - private boolean mReadOnlyApn; - /** - * The APN deletion feature within menu is aligned with the APN adding feature. - * Having only one of them could lead to a UX which not that make sense from user's - * perspective. - * - * mIsAddApnAllowed stores the configuration value reading from - * CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL to support the presentation - * control of the menu options. When false, delete option would be invisible to - * the end user. - */ - private boolean mIsAddApnAllowed; - private Uri mCarrierUri; - private boolean mIsCarrierIdApn; /** * APN types for data connections. These are usage categories for an APN @@ -225,150 +105,41 @@ public class ApnEditor extends SettingsPreferenceFragment APN_TYPE_OEM_PRIVATE, }; - /** - * Standard projection for the interesting columns of a normal note. - */ - private static final String[] sProjection = new String[] { - Telephony.Carriers._ID, // 0 - Telephony.Carriers.NAME, // 1 - Telephony.Carriers.APN, // 2 - Telephony.Carriers.PROXY, // 3 - Telephony.Carriers.PORT, // 4 - Telephony.Carriers.USER, // 5 - Telephony.Carriers.SERVER, // 6 - Telephony.Carriers.PASSWORD, // 7 - Telephony.Carriers.MMSC, // 8 - Telephony.Carriers.MCC, // 9 - Telephony.Carriers.MNC, // 10 - Telephony.Carriers.NUMERIC, // 11 - Telephony.Carriers.MMSPROXY, // 12 - Telephony.Carriers.MMSPORT, // 13 - Telephony.Carriers.AUTH_TYPE, // 14 - Telephony.Carriers.TYPE, // 15 - Telephony.Carriers.PROTOCOL, // 16 - Telephony.Carriers.CARRIER_ENABLED, // 17 - Telephony.Carriers.BEARER, // 18 - Telephony.Carriers.BEARER_BITMASK, // 19 - Telephony.Carriers.ROAMING_PROTOCOL, // 20 - Telephony.Carriers.MVNO_TYPE, // 21 - Telephony.Carriers.MVNO_MATCH_DATA, // 22 - Telephony.Carriers.EDITED_STATUS, // 23 - Telephony.Carriers.USER_EDITABLE, // 24 - Telephony.Carriers.CARRIER_ID // 25 - }; - - private static final int ID_INDEX = 0; - @VisibleForTesting - static final int NAME_INDEX = 1; - @VisibleForTesting - static final int APN_INDEX = 2; - private static final int PROXY_INDEX = 3; - private static final int PORT_INDEX = 4; - private static final int USER_INDEX = 5; - private static final int SERVER_INDEX = 6; - private static final int PASSWORD_INDEX = 7; - private static final int MMSC_INDEX = 8; - @VisibleForTesting - static final int MCC_INDEX = 9; - @VisibleForTesting - static final int MNC_INDEX = 10; - private static final int MMSPROXY_INDEX = 12; - private static final int MMSPORT_INDEX = 13; - private static final int AUTH_TYPE_INDEX = 14; - @VisibleForTesting - static final int TYPE_INDEX = 15; - @VisibleForTesting - static final int PROTOCOL_INDEX = 16; - @VisibleForTesting - static final int CARRIER_ENABLED_INDEX = 17; - private static final int BEARER_INDEX = 18; - private static final int BEARER_BITMASK_INDEX = 19; - @VisibleForTesting - static final int ROAMING_PROTOCOL_INDEX = 20; - private static final int MVNO_TYPE_INDEX = 21; - private static final int MVNO_MATCH_DATA_INDEX = 22; - private static final int EDITED_INDEX = 23; - private static final int USER_EDITABLE_INDEX = 24; - private static final int CARRIER_ID_INDEX = 25; - @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); + maybeRedirectToNewPage(); + finish(); + } + + private void maybeRedirectToNewPage() { if (isUserRestricted()) { Log.e(TAG, "This setting isn't available due to user restriction."); - finish(); return; } - setLifecycleForAllControllers(); - final Intent intent = getIntent(); final String action = intent.getAction(); - if (TextUtils.isEmpty(action)) { - finish(); - return; - } - mSubId = intent.getIntExtra(ApnSettings.SUB_ID, - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - initApnEditorUi(); - getCarrierCustomizedConfig(getContext()); + int subId = + intent.getIntExtra(ApnSettings.SUB_ID, SubscriptionManager.INVALID_SUBSCRIPTION_ID); - Uri uri = null; - if (action.equals(Intent.ACTION_EDIT)) { - uri = intent.getData(); + Uri uri = intent.getData(); + if (Intent.ACTION_EDIT.equals(action)) { if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { Log.e(TAG, "Edit request not for carrier table. Uri: " + uri); - finish(); - return; + } else { + String route = ApnEditPageProvider.INSTANCE.getRoute(EDIT_URL, uri, subId); + SpaActivity.startSpaActivity(requireContext(), route); } - } else if (action.equals(Intent.ACTION_INSERT)) { - mCarrierUri = intent.getData(); - if (!mCarrierUri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { - Log.e(TAG, "Insert request not for carrier table. Uri: " + mCarrierUri); - finish(); - return; + } else if (Intent.ACTION_INSERT.equals(action)) { + if (!uri.isPathPrefixMatch(Telephony.Carriers.CONTENT_URI)) { + Log.e(TAG, "Insert request not for carrier table. Uri: " + uri); + } else { + String route = ApnEditPageProvider.INSTANCE.getRoute( + INSERT_URL, Telephony.Carriers.CONTENT_URI, subId); + SpaActivity.startSpaActivity(getContext(), route); } - mNewApn = true; - mMvnoTypeStr = intent.getStringExtra(ApnSettings.MVNO_TYPE); - mMvnoMatchDataStr = intent.getStringExtra(ApnSettings.MVNO_MATCH_DATA); - } else { - finish(); - return; - } - - // Creates an ApnData to store the apn data temporary, so that we don't need the cursor to - // get the apn data. The uri is null if the action is ACTION_INSERT, that mean there is no - // record in the database, so create a empty ApnData to represent a empty row of database. - if (uri != null) { - mApnData = getApnDataFromUri(uri); - } else { - mApnData = new ApnData(sProjection.length); - } - final int carrierId = mApnData.getInteger(CARRIER_ID_INDEX, - TelephonyManager.UNKNOWN_CARRIER_ID); - mIsCarrierIdApn = (carrierId > TelephonyManager.UNKNOWN_CARRIER_ID); - - final boolean isUserEdited = mApnData.getInteger(EDITED_INDEX, - Telephony.Carriers.USER_EDITED) == Telephony.Carriers.USER_EDITED; - - Log.d(TAG, "onCreate: EDITED " + isUserEdited); - // if it's not a USER_EDITED apn, check if it's read-only - if (!isUserEdited && (mApnData.getInteger(USER_EDITABLE_INDEX, 1) == 0 - || apnTypesMatch(mReadOnlyApnTypes, mApnData.getString(TYPE_INDEX)))) { - Log.d(TAG, "onCreate: apnTypesMatch; read-only APN"); - mReadOnlyApn = true; - disableAllFields(); - } else if (!ArrayUtils.isEmpty(mReadOnlyApnFields)) { - disableFields(mReadOnlyApnFields); - } - // Make sure that a user cannot break carrier id APN matching - if (mIsCarrierIdApn) { - disableFieldsForCarrieridApn(); - } - - for (int i = 0; i < getPreferenceScreen().getPreferenceCount(); i++) { - getPreferenceScreen().getPreference(i).setOnPreferenceChangeListener(this); } } @@ -384,44 +155,6 @@ public class ApnEditor extends SettingsPreferenceFragment return ArrayUtils.concat(String.class, carrierReadOnlyApnTypes, ALWAYS_READ_ONLY_APN_TYPES); } - /** - * Enable ProxySubscriptionMgr with Lifecycle support for all controllers - * live within this fragment - */ - private void setLifecycleForAllControllers() { - if (mProxySubscriptionMgr == null) { - mProxySubscriptionMgr = ProxySubscriptionManager.getInstance(getContext()); - } - mProxySubscriptionMgr.setLifecycle(getLifecycle()); - } - - @Override - public void onViewStateRestored(@Nullable Bundle savedInstanceState) { - super.onViewStateRestored(savedInstanceState); - fillUI(savedInstanceState == null); - setCarrierCustomizedConfigToUi(); - } - - @VisibleForTesting - static String formatInteger(String value) { - try { - final int intValue = Integer.parseInt(value); - return String.format(getCorrectDigitsFormat(value), intValue); - } catch (NumberFormatException e) { - return value; - } - } - - /** - * Get the digits format so we preserve leading 0's. - * MCCs are 3 digits and MNCs are either 2 or 3. - */ - static String getCorrectDigitsFormat(String value) { - if (value.length() == 2) return "%02d"; - else return "%03d"; - } - - /** * Check if passed in array of APN types indicates all APN types * @param apnTypes array of APN types. "*" indicates all types. @@ -447,1045 +180,11 @@ public class ApnEditor extends SettingsPreferenceFragment return true; } - /** - * Check if APN types overlap. - * @param apnTypesArray1 array of APNs. Empty array indicates no APN type; "*" indicates all - * types - * @param apnTypes2 comma separated string of APN types. Empty string represents all types. - * @return if any apn type matches return true, otherwise return false - */ - private boolean apnTypesMatch(String[] apnTypesArray1, String apnTypes2) { - if (ArrayUtils.isEmpty(apnTypesArray1)) { - return false; - } - - final String[] apnTypesArray1LowerCase = new String[apnTypesArray1.length]; - for (int i = 0; i < apnTypesArray1.length; i++) { - apnTypesArray1LowerCase[i] = apnTypesArray1[i].toLowerCase(); - } - - if (hasAllApns(apnTypesArray1LowerCase) || TextUtils.isEmpty(apnTypes2)) { - return true; - } - - final List apnTypesList1 = Arrays.asList(apnTypesArray1LowerCase); - final String[] apnTypesArray2 = apnTypes2.split(","); - - for (String apn : apnTypesArray2) { - if (apnTypesList1.contains(apn.trim().toLowerCase())) { - Log.d(TAG, "apnTypesMatch: true because match found for " + apn.trim()); - return true; - } - } - - Log.d(TAG, "apnTypesMatch: false"); - return false; - } - - /** - * Function to get Preference obj corresponding to an apnField - * @param apnField apn field name for which pref is needed - * @return Preference obj corresponding to passed in apnField - */ - private Preference getPreferenceFromFieldName(String apnField) { - switch (apnField) { - case Telephony.Carriers.NAME: - return mName; - case Telephony.Carriers.APN: - return mApn; - case Telephony.Carriers.PROXY: - return mProxy; - case Telephony.Carriers.PORT: - return mPort; - case Telephony.Carriers.USER: - return mUser; - case Telephony.Carriers.SERVER: - return mServer; - case Telephony.Carriers.PASSWORD: - return mPassword; - case Telephony.Carriers.MMSPROXY: - return mMmsProxy; - case Telephony.Carriers.MMSPORT: - return mMmsPort; - case Telephony.Carriers.MMSC: - return mMmsc; - case Telephony.Carriers.MCC: - return mMcc; - case Telephony.Carriers.MNC: - return mMnc; - case Telephony.Carriers.TYPE: - return mApnType; - case Telephony.Carriers.AUTH_TYPE: - return mAuthType; - case Telephony.Carriers.PROTOCOL: - return mProtocol; - case Telephony.Carriers.ROAMING_PROTOCOL: - return mRoamingProtocol; - case Telephony.Carriers.CARRIER_ENABLED: - return mCarrierEnabled; - case Telephony.Carriers.BEARER: - case Telephony.Carriers.BEARER_BITMASK: - return mBearerMulti; - case Telephony.Carriers.MVNO_TYPE: - return mMvnoType; - case Telephony.Carriers.MVNO_MATCH_DATA: - return mMvnoMatchData; - } - return null; - } - - /** - * Disables given fields so that user cannot modify them - * - * @param apnFields fields to be disabled - */ - private void disableFields(String[] apnFields) { - for (String apnField : apnFields) { - final Preference preference = getPreferenceFromFieldName(apnField); - if (preference != null) { - preference.setEnabled(false); - } - } - } - - /** - * Disables all fields so that user cannot modify the APN - */ - private void disableAllFields() { - mName.setEnabled(false); - mApn.setEnabled(false); - mProxy.setEnabled(false); - mPort.setEnabled(false); - mUser.setEnabled(false); - mServer.setEnabled(false); - mPassword.setEnabled(false); - mMmsProxy.setEnabled(false); - mMmsPort.setEnabled(false); - mMmsc.setEnabled(false); - mMcc.setEnabled(false); - mMnc.setEnabled(false); - mApnType.setEnabled(false); - mAuthType.setEnabled(false); - mProtocol.setEnabled(false); - mRoamingProtocol.setEnabled(false); - mCarrierEnabled.setEnabled(false); - mBearerMulti.setEnabled(false); - mMvnoType.setEnabled(false); - mMvnoMatchData.setEnabled(false); - } - - /** - * Disables fields for a carrier id APN to avoid breaking the match criteria - */ - private void disableFieldsForCarrieridApn() { - mMcc.setEnabled(false); - mMnc.setEnabled(false); - mMvnoType.setEnabled(false); - mMvnoMatchData.setEnabled(false); - } - @Override public int getMetricsCategory() { return SettingsEnums.APN_EDITOR; } - @VisibleForTesting - void fillUI(boolean firstTime) { - if (firstTime) { - // Fill in all the values from the db in both text editor and summary - mName.setText(mApnData.getString(NAME_INDEX)); - mApn.setText(mApnData.getString(APN_INDEX)); - mProxy.setText(mApnData.getString(PROXY_INDEX)); - mPort.setText(mApnData.getString(PORT_INDEX)); - mUser.setText(mApnData.getString(USER_INDEX)); - mServer.setText(mApnData.getString(SERVER_INDEX)); - mPassword.setText(mApnData.getString(PASSWORD_INDEX)); - mMmsProxy.setText(mApnData.getString(MMSPROXY_INDEX)); - mMmsPort.setText(mApnData.getString(MMSPORT_INDEX)); - mMmsc.setText(mApnData.getString(MMSC_INDEX)); - mMcc.setText(mApnData.getString(MCC_INDEX)); - mMnc.setText(mApnData.getString(MNC_INDEX)); - mApnType.setText(mApnData.getString(TYPE_INDEX)); - if (mNewApn) { - final SubscriptionInfo subInfo = - mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); - - // Country code - final String mcc = (subInfo == null) ? null : subInfo.getMccString(); - // Network code - final String mnc = (subInfo == null) ? null : subInfo.getMncString(); - - if (!TextUtils.isEmpty(mcc)) { - // Auto populate MNC and MCC for new entries, based on what SIM reports - mMcc.setText(mcc); - mMnc.setText(mnc); - mCurMnc = mnc; - mCurMcc = mcc; - } - } - final int authVal = mApnData.getInteger(AUTH_TYPE_INDEX, -1); - if (authVal != -1) { - mAuthType.setValueIndex(authVal); - } else { - mAuthType.setValue(null); - } - - mProtocol.setValue(mApnData.getString(PROTOCOL_INDEX)); - mRoamingProtocol.setValue(mApnData.getString(ROAMING_PROTOCOL_INDEX)); - mCarrierEnabled.setChecked(mApnData.getInteger(CARRIER_ENABLED_INDEX, 1) == 1); - mBearerInitialVal = mApnData.getInteger(BEARER_INDEX, 0); - - final HashSet bearers = new HashSet(); - int bearerBitmask = mApnData.getInteger(BEARER_BITMASK_INDEX, 0); - if (bearerBitmask == 0) { - if (mBearerInitialVal == 0) { - bearers.add("" + 0); - } - } else { - int i = 1; - while (bearerBitmask != 0) { - if ((bearerBitmask & 1) == 1) { - bearers.add("" + i); - } - bearerBitmask >>= 1; - i++; - } - } - - if (mBearerInitialVal != 0 && !bearers.contains("" + mBearerInitialVal)) { - // add mBearerInitialVal to bearers - bearers.add("" + mBearerInitialVal); - } - mBearerMulti.setValues(bearers); - - mMvnoType.setValue(mApnData.getString(MVNO_TYPE_INDEX)); - mMvnoMatchData.setEnabled(false); - mMvnoMatchData.setText(mApnData.getString(MVNO_MATCH_DATA_INDEX)); - if (mNewApn && mMvnoTypeStr != null && mMvnoMatchDataStr != null) { - mMvnoType.setValue(mMvnoTypeStr); - mMvnoMatchData.setText(mMvnoMatchDataStr); - } - } - - mName.setSummary(checkNull(mName.getText())); - mApn.setSummary(checkNull(mApn.getText())); - mProxy.setSummary(checkNull(mProxy.getText())); - mPort.setSummary(checkNull(mPort.getText())); - mUser.setSummary(checkNull(mUser.getText())); - mServer.setSummary(checkNull(mServer.getText())); - mPassword.setSummary(starify(mPassword.getText())); - mMmsProxy.setSummary(checkNull(mMmsProxy.getText())); - mMmsPort.setSummary(checkNull(mMmsPort.getText())); - mMmsc.setSummary(checkNull(mMmsc.getText())); - mMcc.setSummary(formatInteger(checkNull(mMcc.getText()))); - mMnc.setSummary(formatInteger(checkNull(mMnc.getText()))); - mApnType.setSummary(checkNull(mApnType.getText())); - - final String authVal = mAuthType.getValue(); - if (authVal != null) { - final int authValIndex = Integer.parseInt(authVal); - mAuthType.setValueIndex(authValIndex); - - final String[] values = getResources().getStringArray(R.array.apn_auth_entries); - mAuthType.setSummary(values[authValIndex]); - } else { - mAuthType.setSummary(sNotSet); - } - - mProtocol.setSummary(checkNull(protocolDescription(mProtocol.getValue(), mProtocol))); - mRoamingProtocol.setSummary( - checkNull(protocolDescription(mRoamingProtocol.getValue(), mRoamingProtocol))); - mBearerMulti.setSummary( - checkNull(bearerMultiDescription(mBearerMulti.getValues()))); - mMvnoType.setSummary( - checkNull(mvnoDescription(mMvnoType.getValue()))); - mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); - // allow user to edit carrier_enabled for some APN - final boolean ceEditable = getResources().getBoolean( - R.bool.config_allow_edit_carrier_enabled); - if (ceEditable) { - mCarrierEnabled.setEnabled(true); - } else { - mCarrierEnabled.setEnabled(false); - } - } - - /** - * Returns the UI choice (e.g., "IPv4/IPv6") corresponding to the given - * raw value of the protocol preference (e.g., "IPV4V6"). If unknown, - * return null. - */ - private String protocolDescription(String raw, ListPreference protocol) { - String uRaw = checkNull(raw).toUpperCase(); - uRaw = uRaw.equals("IPV4") ? "IP" : uRaw; - final int protocolIndex = protocol.findIndexOfValue(uRaw); - if (protocolIndex == -1) { - return null; - } else { - final String[] values = getResources().getStringArray(R.array.apn_protocol_entries); - try { - return values[protocolIndex]; - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - } - - private String bearerMultiDescription(Set raw) { - final String[] values = getResources().getStringArray(R.array.bearer_entries); - final StringBuilder retVal = new StringBuilder(); - boolean first = true; - for (String bearer : raw) { - int bearerIndex = mBearerMulti.findIndexOfValue(bearer); - try { - if (first) { - retVal.append(values[bearerIndex]); - first = false; - } else { - retVal.append(", " + values[bearerIndex]); - } - } catch (ArrayIndexOutOfBoundsException e) { - // ignore - } - } - final String val = retVal.toString(); - if (!TextUtils.isEmpty(val)) { - return val; - } - return null; - } - - private String mvnoDescription(String newValue) { - final int mvnoIndex = mMvnoType.findIndexOfValue(newValue); - final String oldValue = mMvnoType.getValue(); - - if (mvnoIndex == -1) { - return null; - } else { - final String[] values = getResources().getStringArray(R.array.mvno_type_entries); - final boolean mvnoMatchDataUneditable = - mReadOnlyApn || (mReadOnlyApnFields != null - && Arrays.asList(mReadOnlyApnFields) - .contains(Telephony.Carriers.MVNO_MATCH_DATA)); - mMvnoMatchData.setEnabled(!mvnoMatchDataUneditable && mvnoIndex != 0); - if (newValue != null && !newValue.equals(oldValue)) { - if (values[mvnoIndex].equals("SPN")) { - TelephonyManager telephonyManager = (TelephonyManager) - getContext().getSystemService(TelephonyManager.class); - final TelephonyManager telephonyManagerForSubId = - telephonyManager.createForSubscriptionId(mSubId); - if (telephonyManagerForSubId != null) { - telephonyManager = telephonyManagerForSubId; - } - mMvnoMatchData.setText(telephonyManager.getSimOperatorName()); - } else if (values[mvnoIndex].equals("IMSI")) { - final SubscriptionInfo subInfo = - mProxySubscriptionMgr.getAccessibleSubscriptionInfo(mSubId); - final String mcc = (subInfo == null) ? "" : - Objects.toString(subInfo.getMccString(), ""); - final String mnc = (subInfo == null) ? "" : - Objects.toString(subInfo.getMncString(), ""); - mMvnoMatchData.setText(mcc + mnc + "x"); - } else if (values[mvnoIndex].equals("GID")) { - TelephonyManager telephonyManager = (TelephonyManager) - getContext().getSystemService(TelephonyManager.class); - final TelephonyManager telephonyManagerForSubId = - telephonyManager.createForSubscriptionId(mSubId); - if (telephonyManagerForSubId != null) { - telephonyManager = telephonyManagerForSubId; - } - mMvnoMatchData.setText(telephonyManager.getGroupIdLevel1()); - } else { - // mvno type 'none' case. At this time, mvnoIndex should be 0. - mMvnoMatchData.setText(""); - } - } - - try { - return values[mvnoIndex]; - } catch (ArrayIndexOutOfBoundsException e) { - return null; - } - } - } - /** - * Callback when preference status changed. - */ - public boolean onPreferenceChange(Preference preference, Object newValue) { - String key = preference.getKey(); - if (KEY_AUTH_TYPE.equals(key)) { - try { - final int index = Integer.parseInt((String) newValue); - mAuthType.setValueIndex(index); - - final String[] values = getResources().getStringArray(R.array.apn_auth_entries); - mAuthType.setSummary(values[index]); - } catch (NumberFormatException e) { - return false; - } - } else if (KEY_APN_TYPE.equals(key)) { - String data = (TextUtils.isEmpty((String) newValue) - && !ArrayUtils.isEmpty(mDefaultApnTypes)) - ? getEditableApnType(mDefaultApnTypes) : (String) newValue; - if (!TextUtils.isEmpty(data)) { - mApnType.setSummary(data); - } - } else if (KEY_PROTOCOL.equals(key)) { - final String protocol = protocolDescription((String) newValue, mProtocol); - if (protocol == null) { - return false; - } - mProtocol.setSummary(protocol); - mProtocol.setValue((String) newValue); - } else if (KEY_ROAMING_PROTOCOL.equals(key)) { - final String protocol = protocolDescription((String) newValue, mRoamingProtocol); - if (protocol == null) { - return false; - } - mRoamingProtocol.setSummary(protocol); - mRoamingProtocol.setValue((String) newValue); - } else if (KEY_BEARER_MULTI.equals(key)) { - final String bearer = bearerMultiDescription((Set) newValue); - if (bearer == null) { - return false; - } - mBearerMulti.setValues((Set) newValue); - mBearerMulti.setSummary(bearer); - } else if (KEY_MVNO_TYPE.equals(key)) { - final String mvno = mvnoDescription((String) newValue); - if (mvno == null) { - return false; - } - mMvnoType.setValue((String) newValue); - mMvnoType.setSummary(mvno); - mMvnoMatchData.setSummary(checkNullforMvnoValue(mMvnoMatchData.getText())); - } else if (KEY_PASSWORD.equals(key)) { - mPassword.setSummary(starify(newValue != null ? String.valueOf(newValue) : "")); - } else if (KEY_CARRIER_ENABLED.equals(key)) { - // do nothing - } else { - preference.setSummary(checkNull(newValue != null ? String.valueOf(newValue) : null)); - } - return true; - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - super.onCreateOptionsMenu(menu, inflater); - // If it's a new APN, then cancel will delete the new entry in onPause - // If APN add is not allowed, delete might lead to issue regarding recovery - if (!mNewApn && !mReadOnlyApn && mIsAddApnAllowed) { - menu.add(0, MENU_DELETE, 0, R.string.menu_delete) - .setIcon(R.drawable.ic_delete); - } - if (!mReadOnlyApn) { - menu.add(0, MENU_SAVE, 0, R.string.menu_save) - .setIcon(android.R.drawable.ic_menu_save); - } - menu.add(0, MENU_CANCEL, 0, R.string.menu_cancel) - .setIcon(android.R.drawable.ic_menu_close_clear_cancel); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case MENU_DELETE: - deleteApn(); - finish(); - return true; - case MENU_SAVE: - if (validateAndSaveApnData()) { - finish(); - } - return true; - case MENU_CANCEL: - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - view.setOnKeyListener(this); - view.setFocusableInTouchMode(true); - view.requestFocus(); - } - - /** - * Try to save the apn data when pressed the back button. An error message will be displayed if - * the apn data is invalid. - * - * TODO(b/77339593): Try to keep the same behavior between back button and up navigate button. - * We will save the valid apn data to the database when pressed the back button, but discard all - * user changed when pressed the up navigate button. - */ - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_DOWN) return false; - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: { - if (validateAndSaveApnData()) { - finish(); - } - return true; - } - } - return false; - } - - /** - * Add key, value to {@code cv} and compare the value against the value at index in - * {@link #mApnData}. - * - *

- * The key, value will not add to {@code cv} if value is null. - * - * @return true if values are different. {@code assumeDiff} indicates if values can be assumed - * different in which case no comparison is needed. - */ - boolean setStringValueAndCheckIfDiff( - ContentValues cv, String key, String value, boolean assumeDiff, int index) { - final String valueFromLocalCache = mApnData.getString(index); - if (VDBG) { - Log.d(TAG, "setStringValueAndCheckIfDiff: assumeDiff: " + assumeDiff - + " key: " + key - + " value: '" + value - + "' valueFromDb: '" + valueFromLocalCache + "'"); - } - final boolean isDiff = assumeDiff - || !((TextUtils.isEmpty(value) && TextUtils.isEmpty(valueFromLocalCache)) - || (value != null && value.equals(valueFromLocalCache))); - - if (isDiff && value != null) { - cv.put(key, value); - } - return isDiff; - } - - /** - * Add key, value to {@code cv} and compare the value against the value at index in - * {@link #mApnData}. - * - * @return true if values are different. {@code assumeDiff} indicates if values can be assumed - * different in which case no comparison is needed. - */ - boolean setIntValueAndCheckIfDiff( - ContentValues cv, String key, int value, boolean assumeDiff, int index) { - final Integer valueFromLocalCache = mApnData.getInteger(index); - if (VDBG) { - Log.d(TAG, "setIntValueAndCheckIfDiff: assumeDiff: " + assumeDiff - + " key: " + key - + " value: '" + value - + "' valueFromDb: '" + valueFromLocalCache + "'"); - } - - final boolean isDiff = assumeDiff || value != valueFromLocalCache; - if (isDiff) { - cv.put(key, value); - } - return isDiff; - } - - /** - * Validates the apn data and save it to the database if it's valid. - * - *

- * A dialog with error message will be displayed if the APN data is invalid. - * - * @return true if there is no error - */ - @VisibleForTesting - boolean validateAndSaveApnData() { - // Nothing to do if it's a read only APN - if (mReadOnlyApn) { - return true; - } - - final String name = checkNotSet(mName.getText()); - final String apn = checkNotSet(mApn.getText()); - final String mcc = checkNotSet(mMcc.getText()); - final String mnc = checkNotSet(mMnc.getText()); - - final String errorMsg = validateApnData(); - if (errorMsg != null) { - showError(); - return false; - } - - final ContentValues values = new ContentValues(); - // call update() if it's a new APN. If not, check if any field differs from the db value; - // if any diff is found update() should be called - boolean callUpdate = mNewApn; - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.NAME, - name, - callUpdate, - NAME_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.APN, - apn, - callUpdate, - APN_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PROXY, - checkNotSet(mProxy.getText()), - callUpdate, - PROXY_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PORT, - checkNotSet(mPort.getText()), - callUpdate, - PORT_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSPROXY, - checkNotSet(mMmsProxy.getText()), - callUpdate, - MMSPROXY_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSPORT, - checkNotSet(mMmsPort.getText()), - callUpdate, - MMSPORT_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.USER, - checkNotSet(mUser.getText()), - callUpdate, - USER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.SERVER, - checkNotSet(mServer.getText()), - callUpdate, - SERVER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PASSWORD, - checkNotSet(mPassword.getText()), - callUpdate, - PASSWORD_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MMSC, - checkNotSet(mMmsc.getText()), - callUpdate, - MMSC_INDEX); - - final String authVal = mAuthType.getValue(); - if (authVal != null) { - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.AUTH_TYPE, - Integer.parseInt(authVal), - callUpdate, - AUTH_TYPE_INDEX); - } - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.PROTOCOL, - checkNotSet(mProtocol.getValue()), - callUpdate, - PROTOCOL_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.ROAMING_PROTOCOL, - checkNotSet(mRoamingProtocol.getValue()), - callUpdate, - ROAMING_PROTOCOL_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.TYPE, - checkNotSet(getUserEnteredApnType()), - callUpdate, - TYPE_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MCC, - mcc, - callUpdate, - MCC_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MNC, - mnc, - callUpdate, - MNC_INDEX); - - values.put(Telephony.Carriers.NUMERIC, mcc + mnc); - - if (mCurMnc != null && mCurMcc != null) { - if (mCurMnc.equals(mnc) && mCurMcc.equals(mcc)) { - values.put(Telephony.Carriers.CURRENT, 1); - } - } - - final Set bearerSet = mBearerMulti.getValues(); - int bearerBitmask = 0; - for (String bearer : bearerSet) { - if (Integer.parseInt(bearer) == 0) { - bearerBitmask = 0; - break; - } else { - bearerBitmask |= getBitmaskForTech(Integer.parseInt(bearer)); - } - } - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.BEARER_BITMASK, - bearerBitmask, - callUpdate, - BEARER_BITMASK_INDEX); - - int bearerVal; - if (bearerBitmask == 0 || mBearerInitialVal == 0) { - bearerVal = 0; - } else if (bitmaskHasTech(bearerBitmask, mBearerInitialVal)) { - bearerVal = mBearerInitialVal; - } else { - // bearer field was being used but bitmask has changed now and does not include the - // initial bearer value -- setting bearer to 0 but maybe better behavior is to choose a - // random tech from the new bitmask?? - bearerVal = 0; - } - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.BEARER, - bearerVal, - callUpdate, - BEARER_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MVNO_TYPE, - checkNotSet(mMvnoType.getValue()), - callUpdate, - MVNO_TYPE_INDEX); - - callUpdate = setStringValueAndCheckIfDiff(values, - Telephony.Carriers.MVNO_MATCH_DATA, - checkNotSet(mMvnoMatchData.getText()), - callUpdate, - MVNO_MATCH_DATA_INDEX); - - callUpdate = setIntValueAndCheckIfDiff(values, - Telephony.Carriers.CARRIER_ENABLED, - mCarrierEnabled.isChecked() ? 1 : 0, - callUpdate, - CARRIER_ENABLED_INDEX); - - values.put(Telephony.Carriers.EDITED_STATUS, Telephony.Carriers.USER_EDITED); - - if (callUpdate) { - final Uri uri = mApnData.getUri() == null ? mCarrierUri : mApnData.getUri(); - updateApnDataToDatabase(uri, values); - } else { - if (VDBG) Log.d(TAG, "validateAndSaveApnData: not calling update()"); - } - - return true; - } - - private void updateApnDataToDatabase(Uri uri, ContentValues values) { - ThreadUtils.postOnBackgroundThread(() -> { - if (uri.equals(mCarrierUri)) { - // Add a new apn to the database - final Uri newUri = getContentResolver().insert(mCarrierUri, values); - if (newUri == null) { - Log.e(TAG, "Can't add a new apn to database " + mCarrierUri); - } - } else { - // Update the existing apn - getContentResolver().update( - uri, values, null /* where */, null /* selection Args */); - } - }); - } - - /** - * Validates whether the apn data is valid. - * - * @return An error message if the apn data is invalid, otherwise return null. - */ - @VisibleForTesting - String validateApnData() { - String errorMsg = null; - - final String name = checkNotSet(mName.getText()); - final String apn = checkNotSet(mApn.getText()); - final String mcc = checkNotSet(mMcc.getText()); - final String mnc = checkNotSet(mMnc.getText()); - boolean doNotCheckMccMnc = mIsCarrierIdApn && TextUtils.isEmpty(mcc) - && TextUtils.isEmpty(mnc); - if (TextUtils.isEmpty(name)) { - errorMsg = getResources().getString(R.string.error_name_empty); - } else if (TextUtils.isEmpty(apn)) { - errorMsg = getResources().getString(R.string.error_apn_empty); - } else if (doNotCheckMccMnc) { - Log.d(TAG, "validateApnData: carrier id APN does not have mcc/mnc defined"); - // no op, skip mcc mnc null check - } else if (mcc == null || mcc.length() != 3) { - errorMsg = getResources().getString(R.string.error_mcc_not3); - } else if ((mnc == null || (mnc.length() & 0xFFFE) != 2)) { - errorMsg = getResources().getString(R.string.error_mnc_not23); - } - - if (errorMsg == null) { - // if carrier does not allow editing certain apn types, make sure type does not include - // those - if (!ArrayUtils.isEmpty(mReadOnlyApnTypes) - && apnTypesMatch(mReadOnlyApnTypes, getUserEnteredApnType())) { - final StringBuilder stringBuilder = new StringBuilder(); - for (String type : mReadOnlyApnTypes) { - stringBuilder.append(type).append(", "); - Log.d(TAG, "validateApnData: appending type: " + type); - } - // remove last ", " - if (stringBuilder.length() >= 2) { - stringBuilder.delete(stringBuilder.length() - 2, stringBuilder.length()); - } - errorMsg = String.format(getResources().getString(R.string.error_adding_apn_type), - stringBuilder); - } - } - - return errorMsg; - } - - @VisibleForTesting - void showError() { - ErrorDialog.showError(this); - } - - private void deleteApn() { - if (mApnData.getUri() != null) { - getContentResolver().delete(mApnData.getUri(), null, null); - mApnData = new ApnData(sProjection.length); - } - } - - private String starify(String value) { - if (value == null || value.length() == 0) { - return sNotSet; - } else { - final char[] password = new char[value.length()]; - for (int i = 0; i < password.length; i++) { - password[i] = '*'; - } - return new String(password); - } - } - - /** - * Returns {@link #sNotSet} if the given string {@code value} is null or empty. The string - * {@link #sNotSet} typically used as the default display when an entry in the preference is - * null or empty. - */ - private String checkNull(String value) { - return TextUtils.isEmpty(value) ? sNotSet : value; - } - - /** - * To make traslation be diversity, use another string id for MVNO value. - */ - private String checkNullforMvnoValue(String value) { - String notSetForMvnoValue = getResources().getString(R.string.apn_not_set_for_mvno); - return TextUtils.isEmpty(value) ? notSetForMvnoValue : value; - } - - /** - * Returns null if the given string {@code value} equals to {@link #sNotSet}. This method - * should be used when convert a string value from preference to database. - */ - private String checkNotSet(String value) { - return sNotSet.equals(value) ? null : value; - } - - @VisibleForTesting - String getUserEnteredApnType() { - // if user has not specified a type, map it to "ALL APN TYPES THAT ARE NOT READ-ONLY" - // but if user enter empty type, map it just for default - String userEnteredApnType = mApnType.getText(); - if (userEnteredApnType != null) userEnteredApnType = userEnteredApnType.trim(); - if ((TextUtils.isEmpty(userEnteredApnType) - || APN_TYPE_ALL.equals(userEnteredApnType))) { - userEnteredApnType = getEditableApnType(APN_TYPES); - } - Log.d(TAG, "getUserEnteredApnType: changed apn type to editable apn types: " - + userEnteredApnType); - return userEnteredApnType; - } - - private String getEditableApnType(String[] apnTypeList) { - final StringBuilder editableApnTypes = new StringBuilder(); - final List readOnlyApnTypes = Arrays.asList(mReadOnlyApnTypes); - boolean first = true; - for (String apnType : apnTypeList) { - // add APN type if it is not read-only and is not wild-cardable - if (!readOnlyApnTypes.contains(apnType) - && !apnType.equals(APN_TYPE_IA) - && !apnType.equals(APN_TYPE_EMERGENCY) - && !apnType.equals(APN_TYPE_MCX) - && !apnType.equals(APN_TYPE_IMS)) { - if (first) { - first = false; - } else { - editableApnTypes.append(","); - } - editableApnTypes.append(apnType); - } - } - return editableApnTypes.toString(); - } - - private void initApnEditorUi() { - addPreferencesFromResource(R.xml.apn_editor); - - sNotSet = getResources().getString(R.string.apn_not_set); - mName = (EditTextPreference) findPreference("apn_name"); - mApn = (EditTextPreference) findPreference("apn_apn"); - mProxy = (EditTextPreference) findPreference("apn_http_proxy"); - mPort = (EditTextPreference) findPreference("apn_http_port"); - mUser = (EditTextPreference) findPreference("apn_user"); - mServer = (EditTextPreference) findPreference("apn_server"); - mPassword = (EditTextPreference) findPreference(KEY_PASSWORD); - mMmsProxy = (EditTextPreference) findPreference("apn_mms_proxy"); - mMmsPort = (EditTextPreference) findPreference("apn_mms_port"); - mMmsc = (EditTextPreference) findPreference("apn_mmsc"); - mMcc = (EditTextPreference) findPreference("apn_mcc"); - mMnc = (EditTextPreference) findPreference("apn_mnc"); - mApnType = (EditTextPreference) findPreference("apn_type"); - mAuthType = (ListPreference) findPreference(KEY_AUTH_TYPE); - mProtocol = (ListPreference) findPreference(KEY_PROTOCOL); - mRoamingProtocol = (ListPreference) findPreference(KEY_ROAMING_PROTOCOL); - mCarrierEnabled = (TwoStatePreference) findPreference(KEY_CARRIER_ENABLED); - mBearerMulti = (MultiSelectListPreference) findPreference(KEY_BEARER_MULTI); - mMvnoType = (ListPreference) findPreference(KEY_MVNO_TYPE); - mMvnoMatchData = (EditTextPreference) findPreference("mvno_match_data"); - } - - @VisibleForTesting - protected void getCarrierCustomizedConfig(Context context) { - mReadOnlyApn = false; - mReadOnlyApnTypes = null; - mReadOnlyApnFields = null; - mIsAddApnAllowed = true; - - final CarrierConfigManager configManager = (CarrierConfigManager) - context.getSystemService(Context.CARRIER_CONFIG_SERVICE); - if (configManager != null) { - final PersistableBundle b = configManager.getConfigForSubId(mSubId); - if (b != null) { - mReadOnlyApnTypes = getReadOnlyApnTypes(b); - if (!ArrayUtils.isEmpty(mReadOnlyApnTypes)) { - Log.d(TAG, - "onCreate: read only APN type: " + Arrays.toString(mReadOnlyApnTypes)); - } - mReadOnlyApnFields = b.getStringArray( - CarrierConfigManager.KEY_READ_ONLY_APN_FIELDS_STRING_ARRAY); - - mDefaultApnTypes = b.getStringArray( - CarrierConfigManager.KEY_APN_SETTINGS_DEFAULT_APN_TYPES_STRING_ARRAY); - - if (!ArrayUtils.isEmpty(mDefaultApnTypes)) { - Log.d(TAG, "onCreate: default apn types: " + Arrays.toString(mDefaultApnTypes)); - } - - mDefaultApnProtocol = b.getString( - CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_PROTOCOL_STRING); - if (!TextUtils.isEmpty(mDefaultApnProtocol)) { - Log.d(TAG, "onCreate: default apn protocol: " + mDefaultApnProtocol); - } - - mDefaultApnRoamingProtocol = b.getString( - CarrierConfigManager.Apn.KEY_SETTINGS_DEFAULT_ROAMING_PROTOCOL_STRING); - if (!TextUtils.isEmpty(mDefaultApnRoamingProtocol)) { - Log.d(TAG, "onCreate: default apn roaming protocol: " - + mDefaultApnRoamingProtocol); - } - - mIsAddApnAllowed = b.getBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL); - if (!mIsAddApnAllowed) { - Log.d(TAG, "onCreate: not allow to add new APN"); - } - } - } - } - - private void setCarrierCustomizedConfigToUi() { - if (TextUtils.isEmpty(mApnType.getText()) && !ArrayUtils.isEmpty(mDefaultApnTypes)) { - String value = getEditableApnType(mDefaultApnTypes); - mApnType.setText(value); - mApnType.setSummary(value); - } - - String protocol = protocolDescription(mDefaultApnProtocol, mProtocol); - if (TextUtils.isEmpty(mProtocol.getValue()) && !TextUtils.isEmpty(protocol)) { - mProtocol.setValue(mDefaultApnProtocol); - mProtocol.setSummary(protocol); - } - - String roamingProtocol = protocolDescription(mDefaultApnRoamingProtocol, mRoamingProtocol); - if (TextUtils.isEmpty(mRoamingProtocol.getValue()) && !TextUtils.isEmpty(roamingProtocol)) { - mRoamingProtocol.setValue(mDefaultApnRoamingProtocol); - mRoamingProtocol.setSummary(roamingProtocol); - } - } - - /** - * Dialog of error message. - */ - public static class ErrorDialog extends InstrumentedDialogFragment { - /** - * Show error dialog. - */ - public static void showError(ApnEditor editor) { - final ErrorDialog dialog = new ErrorDialog(); - dialog.setTargetFragment(editor, 0); - dialog.show(editor.getFragmentManager(), "error"); - } - - @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - final String msg = ((ApnEditor) getTargetFragment()).validateApnData(); - - return new AlertDialog.Builder(getContext()) - .setTitle(R.string.error_title) - .setPositiveButton(android.R.string.ok, null) - .setMessage(msg) - .create(); - } - - @Override - public int getMetricsCategory() { - return SettingsEnums.DIALOG_APN_EDITOR_ERROR; - } - } - - @VisibleForTesting - ApnData getApnDataFromUri(Uri uri) { - ApnData apnData = null; - try (Cursor cursor = getContentResolver().query( - uri, - sProjection, - null /* selection */, - null /* selectionArgs */, - null /* sortOrder */)) { - if (cursor != null && cursor.moveToFirst()) { - apnData = new ApnData(uri, cursor); - } - } - - if (apnData == null) { - Log.d(TAG, "Can't get apnData from Uri " + uri); - } - - return apnData; - } - @VisibleForTesting boolean isUserRestricted() { UserManager userManager = getContext().getSystemService(UserManager.class); @@ -1502,80 +201,4 @@ public class ApnEditor extends SettingsPreferenceFragment } return false; } - - @VisibleForTesting - static class ApnData { - /** - * The uri correspond to a database row of the apn data. This should be null if the apn - * is not in the database. - */ - Uri mUri; - - /** Each element correspond to a column of the database row. */ - Object[] mData; - - ApnData(int numberOfField) { - mData = new Object[numberOfField]; - } - - ApnData(Uri uri, Cursor cursor) { - mUri = uri; - mData = new Object[cursor.getColumnCount()]; - for (int i = 0; i < mData.length; i++) { - switch (cursor.getType(i)) { - case Cursor.FIELD_TYPE_FLOAT: - mData[i] = cursor.getFloat(i); - break; - case Cursor.FIELD_TYPE_INTEGER: - mData[i] = cursor.getInt(i); - break; - case Cursor.FIELD_TYPE_STRING: - mData[i] = cursor.getString(i); - break; - case Cursor.FIELD_TYPE_BLOB: - mData[i] = cursor.getBlob(i); - break; - default: - mData[i] = null; - } - } - } - - Uri getUri() { - return mUri; - } - - void setUri(Uri uri) { - mUri = uri; - } - - Integer getInteger(int index) { - return (Integer) mData[index]; - } - - Integer getInteger(int index, Integer defaultValue) { - final Integer val = getInteger(index); - return val == null ? defaultValue : val; - } - - String getString(int index) { - return (String) mData[index]; - } - } - - private static int getBitmaskForTech(int radioTech) { - if (radioTech >= 1) { - return (1 << (radioTech - 1)); - } - return 0; - } - - private static boolean bitmaskHasTech(int bearerBitmask, int radioTech) { - if (bearerBitmask == 0) { - return true; - } else if (radioTech >= 1) { - return ((bearerBitmask & (1 << (radioTech - 1))) != 0); - } - return false; - } } diff --git a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java b/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java deleted file mode 100644 index d8895d5ddd6..00000000000 --- a/tests/robotests/src/com/android/settings/network/apn/ApnEditorTest.java +++ /dev/null @@ -1,625 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.network.apn; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.os.PersistableBundle; -import android.os.UserManager; -import android.telephony.CarrierConfigManager; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - -import androidx.fragment.app.FragmentActivity; -import androidx.preference.EditTextPreference; -import androidx.preference.ListPreference; -import androidx.preference.MultiSelectListPreference; -import androidx.preference.SwitchPreference; - -import com.android.settings.R; -import com.android.settings.network.ProxySubscriptionManager; -import com.android.settings.network.apn.ApnEditor.ApnData; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -@RunWith(RobolectricTestRunner.class) -@Config(shadows = { - com.android.settings.testutils.shadow.ShadowFragment.class, -}) -public class ApnEditorTest { - - private static final Object[] APN_DATA = { - 0, /* ID */ - "apn_name" /* apn name */, - "apn.com" /* apn */, - "" /* proxy */, - "" /* port */, - "" /* username */, - "" /* server */, - "" /* password */, - "" /* MMSC */, - "123" /* MCC */, - "456" /* MNC */, - "123456" /* operator numeric */, - "" /* MMS proxy */, - "" /* MMS port */, - 0 /* Authentication type */, - "default,supl,ia" /* APN type */, - "IP" /* APN protocol */, - 1 /* APN enable/disable */, - 0 /* Bearer */, - 0 /* Bearer BITMASK*/, - "IPV6" /* APN roaming protocol */, - "None" /* MVNO type */, - "", /* MVNO value */ - }; - - private static final int CURSOR_INTEGER_INDEX = 0; - private static final int CURSOR_STRING_INDEX = 1; - - private static final Uri APN_URI = Uri.parse("Apn://row/1"); - - @Mock - private Cursor mCursor; - - @Mock - private FragmentActivity mActivity; - @Mock - private UserManager mUserManager; - @Mock - private ProxySubscriptionManager mProxySubscriptionMgr; - @Mock - private CarrierConfigManager mCarrierConfigManager; - @Captor - private ArgumentCaptor mUriCaptor; - - private ApnEditor mApnEditorUT; - private Context mContext; - private Resources mResources; - private PersistableBundle mBundle = new PersistableBundle(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = spy(RuntimeEnvironment.application); - - mResources = mContext.getResources(); - mApnEditorUT = spy(new ApnEditor()); - - doReturn(mActivity).when(mApnEditorUT).getActivity(); - doReturn(mResources).when(mApnEditorUT).getResources(); - doNothing().when(mApnEditorUT).finish(); - doNothing().when(mApnEditorUT).showError(); - doReturn(mContext).when(mApnEditorUT).getContext(); - doReturn(mContext.getTheme()).when(mActivity).getTheme(); - doReturn(mContext.getContentResolver()).when(mActivity).getContentResolver(); - - doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); - doReturn(true).when(mUserManager).isAdminUser(); - doReturn(false).when(mUserManager) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - doReturn(mCarrierConfigManager).when(mContext) - .getSystemService(Context.CARRIER_CONFIG_SERVICE); - doReturn(mBundle).when(mCarrierConfigManager).getConfigForSubId(anyInt()); - - setMockPreference(mContext); - mApnEditorUT.mApnData = new FakeApnData(APN_DATA); - mApnEditorUT.sNotSet = "Not Set"; - } - - @Test - public void testApnEditor_doesNotUseManagedQuery() { - mApnEditorUT.getApnDataFromUri(Mockito.mock(Uri.class)); - - verify(mActivity, never()).managedQuery( - any(Uri.class), - any(String[].class), - any(String.class), - any(String.class)); - - verify(mActivity, never()).managedQuery( - any(Uri.class), - any(String[].class), - any(String.class), - any(String[].class), - any(String.class)); - } - - @Test - public void getApnDataFromUri_emptyCursor_returnsNull() { - var mockContentResolver = mock(ContentResolver.class); - var mockCursor = mock(Cursor.class); - doReturn(mockContentResolver).when(mActivity).getContentResolver(); - when(mockContentResolver.query(any(), any(), any(), any(), any())).thenReturn(mockCursor); - when(mockCursor.moveToFirst()).thenReturn(false); - - var apnData = mApnEditorUT.getApnDataFromUri(mock(Uri.class)); - - assertThat(apnData).isNull(); - } - - @Test - public void testSetStringValue_valueChanged_shouldSetValue() { - // GIVEN an APN value which is different than the APN value in database - final String apnKey = "apn"; - final String apnValue = "testing.com"; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is different than the one in database, and it has been stored in the - // given ContentValues - assertThat(isDiff).isTrue(); - assertThat(apnValue).isEqualTo(cv.getAsString(apnKey)); - } - - @Test - public void testSetStringValue_valueNotChanged_shouldNotSetValue() { - // GIVEN an APN value which is same as the APN value in database - final String apnKey = "apn"; - final String apnValue = (String) APN_DATA[ApnEditor.APN_INDEX]; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is same as the one in database, and the new APN value is not stored - // in the given ContentValues - assertThat(isDiff).isFalse(); - assertThat(cv.get(apnKey)).isNull(); - } - - @Test - public void testSetStringValue_nullValue_shouldNotSetValue_shouldNotSetValue() { - // GIVEN a null APN value - final String apnKey = "apn"; - final String apnValue = null; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn value - final boolean isDiff = mApnEditorUT.setStringValueAndCheckIfDiff( - cv, apnKey, apnValue, false /* assumeDiff */, ApnEditor.APN_INDEX); - - // THEN the APN value is different than the one in database, but the null value is not - // stored in the given ContentValues - assertThat(isDiff).isTrue(); - assertThat(cv.get(apnKey)).isNull(); - } - - @Test - public void testSetIntValue_valueChanged_shouldSetValue() { - // GIVEN a value indicated whether the apn is enabled, and it's different than the value in - // the database - final String apnEnabledKey = "apn_enabled"; - final int apnEnabledValue = 0; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn enabled - final boolean isDiff = mApnEditorUT.setIntValueAndCheckIfDiff( - cv, - apnEnabledKey, - apnEnabledValue, - false /* assumeDiff */, - ApnEditor.CARRIER_ENABLED_INDEX); - - // THEN the apn enabled field is different than the one in database, and it has been stored - // in the given ContentValues - assertThat(isDiff).isTrue(); - assertThat(cv.getAsInteger(apnEnabledKey)).isEqualTo(apnEnabledValue); - } - - @Test - public void testSetIntValue_valueNotChanged_shouldNotSetValue() { - // GIVEN a value indicated whether the apn is enabled, and it's same as the one in the - // database - final String apnEnabledKey = "apn_enabled"; - final int apnEnabledValue = (int) APN_DATA[ApnEditor.CARRIER_ENABLED_INDEX]; - final ContentValues cv = new ContentValues(); - - // WHEN try to check and set the apn enabled - final boolean isDiff = mApnEditorUT.setIntValueAndCheckIfDiff( - cv, - apnEnabledKey, - apnEnabledValue, - false /* assumeDiff */, - ApnEditor.CARRIER_ENABLED_INDEX); - - // THEN the apn enabled field is same as the one in the database, and the filed is not - // stored in the given ContentValues - assertThat(isDiff).isFalse(); - assertThat(cv.get(apnEnabledKey)).isNull(); - } - - @Test - public void testValidateApnData_validData_shouldReturnNull() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message should be null - assertThat(errMsg).isNull(); - } - - @Test - public void testValidateApn_apnNameNotSet_shouldReturnErrorMessage() { - // GIVEN a apn data without the apn name - mApnEditorUT.mApnData.mData[ApnEditor.NAME_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // THEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the apn name not set is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_name_empty)); - } - - @Test - public void testValidateApnData_apnNotSet_shouldReturnErrorMessage() { - // GIVEN a apn data without the apn - mApnEditorUT.mApnData.mData[ApnEditor.APN_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // THEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the apn not set is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_apn_empty)); - } - - @Test - public void testValidateApnData_mccInvalid_shouldReturnErrorMessage() { - // The length of the mcc should be 3 - mApnEditorUT.mApnData.mData[ApnEditor.MCC_INDEX] = "1324"; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the mcc invalid is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_mcc_not3)); - } - - @Test - public void testValidateApnData_mncInvalid_shouldReturnErrorMessage() { - // GIVEN an apn data with invalid mnc - // The length of the mnc should be 2 or 3 - mApnEditorUT.mApnData.mData[ApnEditor.MNC_INDEX] = "1324"; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN validate the apn data - final String errMsg = mApnEditorUT.validateApnData(); - - // THEN the error message indicated the mnc invalid is returned - assertThat(errMsg).isEqualTo(mResources.getString(R.string.error_mnc_not23)); - } - - @Test - public void testSaveApnData_pressBackButtonWithValidApnData_shouldSaveApnData() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the back button - final KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK); - mApnEditorUT.onKey(new View(mContext), KeyEvent.KEYCODE_BACK, event); - - // THEN the apn data is saved and the apn editor is closed - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).finish(); - } - - @Test - public void testSaveApnData_pressSaveButtonWithValidApnData_shouldSaveApnData() { - // GIVEN a valid apn data - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the save button - MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST + 1 indicated the SAVE option in ApnEditor - doReturn(Menu.FIRST + 1).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the apn data is saved and the apn editor is closed - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).finish(); - } - - @Test - public void testSaveApnData_apnDataInvalid_shouldNotSaveApnData() { - // GIVEN an invalid apn data - // The valid apn data should contains a non-empty apn name - mApnEditorUT.mApnData.mData[ApnEditor.NAME_INDEX] = ""; - mApnEditorUT.fillUI(true /* firstTime */); - - // WHEN press the save button - final MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST + 1 indicated the SAVE option in ApnEditor - doReturn(Menu.FIRST + 1).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the error dialog is shown - verify(mApnEditorUT).validateAndSaveApnData(); - verify(mApnEditorUT).showError(); - } - - @Test - public void testDeleteApnData_shouldDeleteData() { - // GIVEN a valid apn data correspond a row in database - final Uri apnUri = Uri.parse("content://telephony/carriers/1"); - mApnEditorUT.mApnData = new FakeApnData(APN_DATA, apnUri); - mApnEditorUT.fillUI(true /* firstTime */); - ContentResolver mockContentResolver = Mockito.mock(ContentResolver.class); - doReturn(mockContentResolver).when(mActivity).getContentResolver(); - - // WHEN press the save button - final MenuItem item = Mockito.mock(MenuItem.class); - // Menu.FIRST indicated the DELETE option in ApnEditor - doReturn(Menu.FIRST).when(item).getItemId(); - mApnEditorUT.onOptionsItemSelected(item); - - // THEN the apn data is deleted and the apn editor is closed - verify(mockContentResolver).delete(mUriCaptor.capture(), any(), any()); - assertThat(apnUri).isEqualTo(mUriCaptor.getValue()); - verify(mApnEditorUT).finish(); - } - - @Test - public void testDeleteApnData_shouldNotPresentMenuWhenNotSupportAdding() { - mBundle.putBoolean(CarrierConfigManager.KEY_ALLOW_ADDING_APNS_BOOL, false); - - MenuItem item = Mockito.mock(MenuItem.class); - Menu menu = Mockito.mock(Menu.class); - doReturn(item).when(menu).add(anyInt(), anyInt(), anyInt(), anyInt()); - - mApnEditorUT.getCarrierCustomizedConfig(mContext); - mApnEditorUT.onCreateOptionsMenu(menu, null); - - verify(menu, times(0)).add(anyInt(), eq(ApnEditor.MENU_DELETE), anyInt(), anyInt()); - } - - @Test(expected = ClassCastException.class) - public void testApnData_invalidIntegerType_throwsInvalidTypeException() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get a string from an integer column - // THEN the InvalidTypeException is threw - data.getString(CURSOR_INTEGER_INDEX); - } - - @Test(expected = ClassCastException.class) - public void testApnData_invalidStringType_throwsInvalidTypeException() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get a integer from a string column - // THEN the InvalidTypeException is threw - data.getInteger(CURSOR_STRING_INDEX); - } - - @Test - public void testApnData_validIntegerType_returnCorrectValue() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get integer from an integer column - final int val = data.getInteger(CURSOR_INTEGER_INDEX); - - // THEN the integer is returned correctly - assertThat(val).isEqualTo(mCursor.getInt(CURSOR_INTEGER_INDEX)); - } - - @Test - public void testApnData_validStringType_returnCorrectValue() { - // GIVEN a ApnData constructed from cursor - initCursor(); - final ApnData data = new ApnData(APN_URI, mCursor); - - // WHEN get string from a string column - final String str = data.getString(CURSOR_STRING_INDEX); - - // THEN the integer is returned correctly - assertThat(str).isEqualTo(mCursor.getString(CURSOR_STRING_INDEX)); - } - - @Test - public void testApnData_nullValueColumn_returnNull() { - // GIVEN a empty ApnData - final ApnData data = new ApnData(3); - - // WHEN get string value from a null column - final String str = data.getString(0); - - // THEN the null value is returned - assertThat(str).isNull(); - } - - @Test - public void formatInteger_shouldParseString() { - assertThat(ApnEditor.formatInteger("42")).isEqualTo("42"); - assertThat(ApnEditor.formatInteger("01")).isEqualTo("01"); - assertThat(ApnEditor.formatInteger("001")).isEqualTo("001"); - } - - @Test - public void formatInteger_shouldIgnoreNonIntegers() { - assertThat(ApnEditor.formatInteger("not an int")).isEqualTo("not an int"); - } - - @Test - public void onCreate_notAdminUser_shouldFinish() { - doReturn(false).when(mUserManager).isAdminUser(); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void onCreate_hasUserRestriction_shouldFinish() { - doReturn(true).when(mUserManager) - .hasUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void onCreate_noAction_shouldFinishAndNoCrash() { - ProxySubscriptionManager proxySubscriptionMgr = mock(ProxySubscriptionManager.class); - mApnEditorUT.mProxySubscriptionMgr = proxySubscriptionMgr; - doReturn(new Intent()).when(mActivity).getIntent(); - doNothing().when(mApnEditorUT).addPreferencesFromResource(anyInt()); - - mApnEditorUT.onCreate(null); - - verify(mApnEditorUT).finish(); - } - - @Test - public void testOnViewStateRestored_customizedValueWithoutDefault_shouldShowCustomized() { - mApnEditorUT.mDefaultApnProtocol = "IP"; - mApnEditorUT.mApnData.mData[ApnEditor.PROTOCOL_INDEX] = null; - mApnEditorUT.mProtocol.setEntryValues(new CharSequence[]{"IP", "IPV6", "IPV4V6"}); - - mApnEditorUT.onViewStateRestored(null); - - assertThat(mApnEditorUT.mProtocol.getSummary()).isEqualTo("IPv4"); - } - - @Test - public void testOnViewStateRestored_customizedValueWithDefault_shouldShowDefault() { - mApnEditorUT.mDefaultApnProtocol = "IP"; - mApnEditorUT.mApnData.mData[ApnEditor.PROTOCOL_INDEX] = "IPV6"; - mApnEditorUT.mProtocol.setEntryValues(new CharSequence[]{"IP", "IPV6", "IPV4V6"}); - - mApnEditorUT.onViewStateRestored(null); - - assertThat(mApnEditorUT.mProtocol.getSummary()).isEqualTo("IPv6"); - } - - @Test - public void getUserEnteredApnType_emptyApnType_shouldReturnDefault() { - // case 1 - // GIVEN read only APN types with DUN - mApnEditorUT.mReadOnlyApnTypes = new String [] {"dun"}; - // GIVEN read specificApnTypeForEmptyInput with DEFAULT,DUN - mApnEditorUT.mDefaultApnTypes = new String [] {"default", "dun"}; - - // Input empty in TYPE - mApnEditorUT.mApnData.mData[ApnEditor.TYPE_INDEX] = ""; - mApnEditorUT.onViewStateRestored(null); - - // THEN APN type should be default - assertThat(mApnEditorUT.getUserEnteredApnType()).isEqualTo("default"); - - // case 2 - // GIVEN read only APN types with DUN - mApnEditorUT.mReadOnlyApnTypes = new String [] {"dun"}; - // GIVEN read specificApnTypeForEmptyInput with DEFAULT - mApnEditorUT.mDefaultApnTypes = new String [] {"default"}; - - // Input empty in TYPE - mApnEditorUT.mApnData.mData[ApnEditor.TYPE_INDEX] = ""; - mApnEditorUT.onViewStateRestored(null); - - // THEN APN type should be default - assertThat(mApnEditorUT.getUserEnteredApnType()).isEqualTo("default"); - } - - private void initCursor() { - doReturn(2).when(mCursor).getColumnCount(); - doReturn(2).when(mCursor).getInt(CURSOR_INTEGER_INDEX); - doReturn("str").when(mCursor).getString(CURSOR_STRING_INDEX); - doReturn(Cursor.FIELD_TYPE_INTEGER).when(mCursor).getType(CURSOR_INTEGER_INDEX); - doReturn(Cursor.FIELD_TYPE_STRING).when(mCursor).getType(CURSOR_STRING_INDEX); - } - - private void setMockPreference(Context context) { - mApnEditorUT.mName = new EditTextPreference(context); - mApnEditorUT.mApn = new EditTextPreference(context); - mApnEditorUT.mProxy = new EditTextPreference(context); - mApnEditorUT.mPort = new EditTextPreference(context); - mApnEditorUT.mUser = new EditTextPreference(context); - mApnEditorUT.mServer = new EditTextPreference(context); - mApnEditorUT.mPassword = new EditTextPreference(context); - mApnEditorUT.mMmsc = new EditTextPreference(context); - mApnEditorUT.mMcc = new EditTextPreference(context); - mApnEditorUT.mMnc = new EditTextPreference(context); - mApnEditorUT.mMmsProxy = new EditTextPreference(context); - mApnEditorUT.mMmsPort = new EditTextPreference(context); - mApnEditorUT.mAuthType = new ListPreference(context); - mApnEditorUT.mApnType = new EditTextPreference(context); - mApnEditorUT.mProtocol = new ListPreference(context); - mApnEditorUT.mRoamingProtocol = new ListPreference(context); - mApnEditorUT.mCarrierEnabled = new SwitchPreference(context); - mApnEditorUT.mBearerMulti = new MultiSelectListPreference(context); - mApnEditorUT.mMvnoType = new ListPreference(context); - mApnEditorUT.mMvnoMatchData = new EditTextPreference(context); - } - - private final class FakeApnData extends ApnData { - FakeApnData(Object[] data) { - super(data.length); - System.arraycopy(data, 0, mData, 0, data.length); - } - - FakeApnData(Object[] data, Uri uri) { - this(data); - mUri = uri; - } - } -} From 8697bdfb259092bb2a9cf74ae3b673c43b2c3763 Mon Sep 17 00:00:00 2001 From: Jacky Wang Date: Sat, 19 Oct 2024 21:32:35 +0800 Subject: [PATCH 4/7] Enable catalyst test for BluetoothDashboardScreenTest Bug: 372774767 Flag: EXEMPT test Test: atest Change-Id: Ic9feb40a94da70d34800ee5f01be8e8bb067b431 --- .../BluetoothDashboardScreenTest.kt | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt index 39c029467b4..16db8413fa7 100644 --- a/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt +++ b/tests/robotests/src/com/android/settings/connecteddevice/BluetoothDashboardScreenTest.kt @@ -15,7 +15,12 @@ */ package com.android.settings.connecteddevice +import android.content.Intent +import android.provider.Settings.Global +import androidx.preference.PreferenceFragmentCompat +import androidx.test.core.app.ActivityScenario import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.Settings.BluetoothDashboardActivity import com.android.settings.flags.Flags import com.android.settingslib.preference.CatalystScreenTestCase import com.google.common.truth.Truth.assertThat @@ -34,6 +39,18 @@ class BluetoothDashboardScreenTest : CatalystScreenTestCase() { assertThat(preferenceScreenCreator.key).isEqualTo(BluetoothDashboardScreen.KEY) } - override fun migration() { + override fun launchFragment( + fragmentClass: Class, + action: (PreferenceFragmentCompat) -> Unit, + ) { + Global.putInt(appContext.contentResolver, Global.DEVICE_PROVISIONED, 1) + val intent = Intent(appContext, BluetoothDashboardActivity::class.java) + ActivityScenario.launch(intent).use { + it.onActivity { activity -> + val fragment = activity.supportFragmentManager.fragments[0] + assertThat(fragment.javaClass).isEqualTo(fragmentClass) + action(fragment as PreferenceFragmentCompat) + } + } } } From bd44c8604130fd6ca80f5109460405800843c71b Mon Sep 17 00:00:00 2001 From: "Pechetty Sravani (xWF)" Date: Mon, 21 Oct 2024 06:18:31 +0000 Subject: [PATCH 5/7] Revert "Fix force close for updating UI after activity destroyed." This reverts commit 2545f06558bebe579bce98b5e81139423d131d2f. Reason for revert: DroidMonitor created revert due to b/374650528.Will be verified through ABTD for standard investigation. Change-Id: Ifa3f061b7954dc2b5780b768fefb9358c597fdc0 --- .../settings/network/telephony/NetworkSelectSettings.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/com/android/settings/network/telephony/NetworkSelectSettings.java b/src/com/android/settings/network/telephony/NetworkSelectSettings.java index a3485cd6c3a..a16f4b5d2d7 100644 --- a/src/com/android/settings/network/telephony/NetworkSelectSettings.java +++ b/src/com/android/settings/network/telephony/NetworkSelectSettings.java @@ -368,11 +368,6 @@ public class NetworkSelectSettings extends DashboardFragment { @VisibleForTesting protected void scanResultHandler(NetworkScanRepository.NetworkScanResult results) { - if (isFinishingOrDestroyed()) { - Log.d(TAG, "scanResultHandler: activity isFinishingOrDestroyed, directly return"); - return; - } - mCellInfoList = filterOutSatellitePlmn(results.getCellInfos()); Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); updateAllPreferenceCategory(); From 1732fa8db02f9e270067a707e836308159683388 Mon Sep 17 00:00:00 2001 From: mxyyiyi Date: Wed, 16 Oct 2024 12:52:11 +0800 Subject: [PATCH 6/7] Redesign the update logic of Allow Background Usage Page. - Use one Controller to manage preferences related to battery Optimization Mode. - Move optimization mode & preferences status update logic from Fragment to Controller. Bug: 373544647 Test: atest AdvancedPowerUsageDetailTest PowerBackgroundUsageDetailTest BatteryOptimizationModePreferenceControllerTest BackgroundUsageAllowabilityPreferenceControllerTest Flag: EXEMPT for simple fix Change-Id: I2b1fed837fb4904e4118ab51c1d0cda36c0f6198 --- res/xml/power_background_usage_detail.xml | 31 +-- res/xml/power_usage_detail.xml | 8 +- .../fuelgauge/AdvancedPowerUsageDetail.java | 104 ++------- .../AllowBackgroundPreferenceController.java | 85 -------- ...UsageAllowabilityPreferenceController.java | 140 ++++++++++++ ...yOptimizationModePreferenceController.java | 136 ++++++++++++ .../OptimizedPreferenceController.java | 66 ------ .../fuelgauge/PowerBackgroundUsageDetail.java | 133 +++--------- .../UnrestrictedPreferenceController.java | 66 ------ .../AdvancedPowerUsageDetailTest.java | 129 ++++------- ...lowBackgroundPreferenceControllerTest.java | 150 ------------- ...eAllowabilityPreferenceControllerTest.java | 202 ++++++++++++++++++ ...imizationModePreferenceControllerTest.java | 179 ++++++++++++++++ .../OptimizedPreferenceControllerTest.java | 130 ----------- .../PowerBackgroundUsageDetailTest.java | 149 +++++-------- .../UnrestrictedPreferenceControllerTest.java | 148 ------------- 16 files changed, 816 insertions(+), 1040 deletions(-) delete mode 100644 src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java create mode 100644 src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java create mode 100644 src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java delete mode 100644 src/com/android/settings/fuelgauge/OptimizedPreferenceController.java delete mode 100644 src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java create mode 100644 tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java delete mode 100644 tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java diff --git a/res/xml/power_background_usage_detail.xml b/res/xml/power_background_usage_detail.xml index 5c7b6a5235a..32d80b52ecb 100644 --- a/res/xml/power_background_usage_detail.xml +++ b/res/xml/power_background_usage_detail.xml @@ -25,22 +25,25 @@ android:layout="@layout/settings_entity_header" android:selectable="false"/> - + - + - + + + + + android:key="background_usage_allowability_category" + settings:controller="com.android.settings.fuelgauge.BackgroundUsageAllowabilityPreferenceController"> + android:key="background_usage_allowability_switch" + android:title="@string/manager_battery_usage_allow_background_usage_title"/> diff --git a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java index e922f7058be..28d7d58bee3 100644 --- a/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java +++ b/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetail.java @@ -26,13 +26,10 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; import android.view.View; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.SettingsActivity; @@ -48,7 +45,6 @@ import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.overlay.FeatureFactory; import com.android.settings.widget.EntityHeaderController; -import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; @@ -67,9 +63,7 @@ import java.util.concurrent.Executors; * 2. Battery related controls for app(i.e uninstall, force stop) */ public class AdvancedPowerUsageDetail extends DashboardFragment - implements ButtonActionDialogFragment.AppButtonsDialogListener, - Preference.OnPreferenceClickListener, - Preference.OnPreferenceChangeListener { + implements ButtonActionDialogFragment.AppButtonsDialogListener { public static final String TAG = "AdvancedPowerDetail"; public static final String EXTRA_UID = "extra_uid"; public static final String EXTRA_PACKAGE_NAME = "extra_package_name"; @@ -86,7 +80,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; private static final String KEY_PREF_HEADER = "header_view"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + private static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_CATEGORY = + "background_usage_allowability_category"; private static final int REQUEST_UNINSTALL = 0; private static final int REQUEST_REMOVE_DEVICE_ADMIN = 1; @@ -96,11 +91,9 @@ public class AdvancedPowerUsageDetail extends DashboardFragment private AppButtonsPreferenceController mAppButtonsPreferenceController; private PowerUsageTimeController mPowerUsageTimeController; - @VisibleForTesting LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState; @VisibleForTesting ApplicationsState.AppEntry mAppEntry; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - @VisibleForTesting PrimarySwitchPreference mAllowBackgroundUsagePreference; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode int mOptimizationMode = BatteryOptimizeUtils.MODE_UNKNOWN; @@ -242,17 +235,11 @@ public class AdvancedPowerUsageDetail extends DashboardFragment public void onAttach(Activity activity) { super.onAttach(activity); + final Bundle bundle = getArguments(); + final int uid = bundle.getInt(EXTRA_UID, 0); + final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); + mBatteryOptimizeUtils = new BatteryOptimizeUtils(getContext(), uid, packageName); mState = ApplicationsState.getInstance(getActivity().getApplication()); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); - onCreateBackgroundUsageState(packageName); - mHeaderPreference = findPreference(KEY_PREF_HEADER); - if (packageName != null) { mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); } @@ -264,7 +251,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment initHeader(); mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); - initFooter(); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); } @@ -299,7 +285,8 @@ public class AdvancedPowerUsageDetail extends DashboardFragment @VisibleForTesting void initHeader() { - final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); + final LayoutPreference headerPreference = findPreference(KEY_PREF_HEADER); + final View appSnippet = headerPreference.findViewById(R.id.entity_header); final Activity context = getActivity(); final Bundle bundle = getArguments(); EntityHeaderController controller = @@ -340,31 +327,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment controller.done(true /* rebindActions */); } - @VisibleForTesting - void initFooter() { - final String stateString; - final String detailInfoString; - final Context context = getContext(); - - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - // Present optimized only string when the package name is invalid. - stateString = context.getString(R.string.manager_battery_usage_optimized_only); - detailInfoString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - // Present unrestricted only string when the package is system or default active app. - stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); - detailInfoString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else { - // Present default string to normal app. - detailInfoString = - context.getString( - R.string.manager_battery_usage_allow_background_usage_summary); - } - mAllowBackgroundUsagePreference.setSummary(detailInfoString); - } - @Override public int getMetricsCategory() { return SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL; @@ -384,7 +346,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment protected List createPreferenceControllers(Context context) { final List controllers = new ArrayList<>(); final Bundle bundle = getArguments(); - final int uid = bundle.getInt(EXTRA_UID, 0); final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); mAppButtonsPreferenceController = @@ -401,7 +362,12 @@ public class AdvancedPowerUsageDetail extends DashboardFragment controllers.add(mPowerUsageTimeController); } controllers.add(mAppButtonsPreferenceController); - controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); + controllers.add( + new BackgroundUsageAllowabilityPreferenceController( + context, + /* dashboardFragment= */ this, + KEY_BACKGROUND_USAGE_ALLOWABILITY_CATEGORY, + mBatteryOptimizeUtils)); return controllers; } @@ -421,34 +387,6 @@ public class AdvancedPowerUsageDetail extends DashboardFragment } } - @Override - public boolean onPreferenceClick(Preference preference) { - if (!(preference instanceof PrimarySwitchPreference) - || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { - return false; - } - PowerBackgroundUsageDetail.startPowerBackgroundUsageDetailPage( - getContext(), getArguments()); - return true; - } - - @Override - public boolean onPreferenceChange(@NonNull Preference preference, Object newValue) { - if (!(preference instanceof PrimarySwitchPreference) - || !TextUtils.equals(preference.getKey(), KEY_ALLOW_BACKGROUND_USAGE)) { - return false; - } - if (newValue instanceof Boolean) { - final boolean isAllowBackgroundUsage = (boolean) newValue; - mBatteryOptimizeUtils.setAppUsageState( - isAllowBackgroundUsage - ? BatteryOptimizeUtils.MODE_OPTIMIZED - : BatteryOptimizeUtils.MODE_RESTRICTED, - Action.APPLY); - } - return true; - } - private void logMetricCategory(int currentOptimizeMode) { if (currentOptimizeMode == mOptimizationMode) { return; @@ -482,16 +420,4 @@ public class AdvancedPowerUsageDetail extends DashboardFragment getArguments().getInt(EXTRA_POWER_USAGE_AMOUNT)); }); } - - private void onCreateBackgroundUsageState(String packageName) { - mAllowBackgroundUsagePreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); - if (mAllowBackgroundUsagePreference != null) { - mAllowBackgroundUsagePreference.setOnPreferenceClickListener(this); - mAllowBackgroundUsagePreference.setOnPreferenceChangeListener(this); - } - - mBatteryOptimizeUtils = - new BatteryOptimizeUtils( - getContext(), getArguments().getInt(EXTRA_UID), packageName); - } } diff --git a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java b/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java deleted file mode 100644 index 52cec79599a..00000000000 --- a/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceController.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.PrimarySwitchPreference; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.MainSwitchPreference; - -/** Controller to update the app background usage state */ -public class AllowBackgroundPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "AllowBackgroundPreferenceController"; - - @VisibleForTesting static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; - - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public AllowBackgroundPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - private void setChecked(Preference preference, boolean checked) { - if (preference instanceof PrimarySwitchPreference) { - ((PrimarySwitchPreference) preference).setChecked(checked); - } else if (preference instanceof MainSwitchPreference) { - ((MainSwitchPreference) preference).setChecked(checked); - } - } - - private void setEnabled(Preference preference, boolean enabled) { - if (preference instanceof PrimarySwitchPreference) { - ((PrimarySwitchPreference) preference).setEnabled(enabled); - ((PrimarySwitchPreference) preference).setSwitchEnabled(enabled); - } else if (preference instanceof MainSwitchPreference) { - ((MainSwitchPreference) preference).setEnabled(enabled); - } - } - - @Override - public void updateState(Preference preference) { - setEnabled(preference, mBatteryOptimizeUtils.isOptimizeModeMutable()); - - final boolean isAllowBackground = - mBatteryOptimizeUtils.getAppOptimizationMode() - != BatteryOptimizeUtils.MODE_RESTRICTED; - setChecked(preference, isAllowBackground); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_ALLOW_BACKGROUND_USAGE; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java b/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java new file mode 100644 index 00000000000..bce439bad1f --- /dev/null +++ b/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceController.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.R; +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settingslib.PrimarySwitchPreference; + +/** Controller to update the manage battery usage preference in App Battery Usage page */ +public class BackgroundUsageAllowabilityPreferenceController extends BasePreferenceController + implements PreferenceControllerMixin { + + @VisibleForTesting + static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH = + "background_usage_allowability_switch"; + + private final BatteryOptimizeUtils mBatteryOptimizeUtils; + private final DashboardFragment mDashboardFragment; + @Nullable @VisibleForTesting PrimarySwitchPreference mBackgroundUsageAllowabilityPreference; + + public BackgroundUsageAllowabilityPreferenceController( + @NonNull Context context, + @NonNull DashboardFragment dashboardFragment, + @NonNull String preferenceKey, + @NonNull BatteryOptimizeUtils batteryOptimizeUtils) { + super(context, preferenceKey); + mDashboardFragment = dashboardFragment; + mBatteryOptimizeUtils = batteryOptimizeUtils; + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(@NonNull Preference preference) { + updatePreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mBackgroundUsageAllowabilityPreference = + screen.findPreference(KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH); + initPreferences(); + } + + @VisibleForTesting + void initPreferences() { + if (mBackgroundUsageAllowabilityPreference == null) { + return; + } + final String stateString; + final String detailInfoString; + boolean isPreferenceEnabled = true; + if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { + // Present "Optimized" only string if the package name is invalid. + stateString = mContext.getString(R.string.manager_battery_usage_optimized_only); + detailInfoString = + mContext.getString(R.string.manager_battery_usage_footer_limited, stateString); + isPreferenceEnabled = false; + } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { + // Present "Unrestricted" only string if the package is system important apps. + stateString = mContext.getString(R.string.manager_battery_usage_unrestricted_only); + detailInfoString = + mContext.getString(R.string.manager_battery_usage_footer_limited, stateString); + isPreferenceEnabled = false; + } else { + // Present default string to normal app. + detailInfoString = + mContext.getString( + R.string.manager_battery_usage_allow_background_usage_summary); + } + mBackgroundUsageAllowabilityPreference.setEnabled(isPreferenceEnabled); + mBackgroundUsageAllowabilityPreference.setSwitchEnabled(isPreferenceEnabled); + mBackgroundUsageAllowabilityPreference.setSummary(detailInfoString); + if (isPreferenceEnabled) { + mBackgroundUsageAllowabilityPreference.setOnPreferenceClickListener( + preference -> { + PowerBackgroundUsageDetail.startPowerBackgroundUsageDetailPage( + mContext, mDashboardFragment.getArguments()); + return true; + }); + mBackgroundUsageAllowabilityPreference.setOnPreferenceChangeListener( + (preference, isAllowBackground) -> { + handleBatteryOptimizeModeUpdated( + (boolean) isAllowBackground + ? BatteryOptimizeUtils.MODE_OPTIMIZED + : BatteryOptimizeUtils.MODE_RESTRICTED); + return true; + }); + } + } + + @VisibleForTesting + void handleBatteryOptimizeModeUpdated(int newBatteryOptimizeMode) { + if (mBatteryOptimizeUtils.getAppOptimizationMode() == newBatteryOptimizeMode) { + Log.w(TAG, "ignore same mode for: " + mBatteryOptimizeUtils.getPackageName()); + return; + } + mBatteryOptimizeUtils.setAppUsageState( + newBatteryOptimizeMode, BatteryOptimizeHistoricalLogEntry.Action.APPLY); + updatePreferences(newBatteryOptimizeMode); + } + + @VisibleForTesting + void updatePreferences(int optimizationMode) { + if (mBackgroundUsageAllowabilityPreference == null) { + return; + } + mBackgroundUsageAllowabilityPreference.setChecked( + optimizationMode != BatteryOptimizeUtils.MODE_RESTRICTED); + } +} diff --git a/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java b/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java new file mode 100644 index 00000000000..0a4cbac4dd6 --- /dev/null +++ b/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceController.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import android.content.Context; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.preference.Preference; +import androidx.preference.PreferenceScreen; + +import com.android.settings.core.BasePreferenceController; +import com.android.settings.core.PreferenceControllerMixin; +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +/** Controller to update the app background usage mode state in Allow background usage page */ +public class BatteryOptimizationModePreferenceController extends BasePreferenceController + implements PreferenceControllerMixin { + + @VisibleForTesting + static final String KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH = + "background_usage_allowability_switch"; + + @VisibleForTesting static final String KEY_OPTIMIZED_PREF = "optimized_preference"; + @VisibleForTesting static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference"; + + private final BatteryOptimizeUtils mBatteryOptimizeUtils; + @Nullable @VisibleForTesting MainSwitchPreference mBackgroundUsageAllowabilityPreference; + @Nullable @VisibleForTesting SelectorWithWidgetPreference mOptimizedPreference; + @Nullable @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference; + + public BatteryOptimizationModePreferenceController( + @NonNull Context context, + @NonNull String preferenceKey, + @NonNull BatteryOptimizeUtils batteryOptimizeUtils) { + super(context, preferenceKey); + mBatteryOptimizeUtils = batteryOptimizeUtils; + } + + @Override + public int getAvailabilityStatus() { + return AVAILABLE; + } + + @Override + public void updateState(@NonNull Preference preference) { + updatePreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Override + public void displayPreference(@NonNull PreferenceScreen screen) { + super.displayPreference(screen); + mBackgroundUsageAllowabilityPreference = + screen.findPreference(KEY_BACKGROUND_USAGE_ALLOWABILITY_SWITCH); + mOptimizedPreference = screen.findPreference(KEY_OPTIMIZED_PREF); + mUnrestrictedPreference = screen.findPreference(KEY_UNRESTRICTED_PREF); + initPreferences(); + } + + @VisibleForTesting + void initPreferences() { + if (mBackgroundUsageAllowabilityPreference == null + || mOptimizedPreference == null + || mUnrestrictedPreference == null) { + return; + } + final boolean isEnabled = mBatteryOptimizeUtils.isOptimizeModeMutable(); + mBackgroundUsageAllowabilityPreference.setEnabled(isEnabled); + mOptimizedPreference.setEnabled(isEnabled); + mUnrestrictedPreference.setEnabled(isEnabled); + if (isEnabled) { + mBackgroundUsageAllowabilityPreference.setOnPreferenceChangeListener( + (preference, isAllowBackground) -> { + handleBatteryOptimizeModeUpdated( + (boolean) isAllowBackground + ? BatteryOptimizeUtils.MODE_OPTIMIZED + : BatteryOptimizeUtils.MODE_RESTRICTED); + return true; + }); + mOptimizedPreference.setOnPreferenceClickListener( + preference -> { + handleBatteryOptimizeModeUpdated(BatteryOptimizeUtils.MODE_OPTIMIZED); + return true; + }); + mUnrestrictedPreference.setOnPreferenceClickListener( + preference -> { + handleBatteryOptimizeModeUpdated(BatteryOptimizeUtils.MODE_UNRESTRICTED); + return true; + }); + } + } + + @VisibleForTesting + void updatePreferences(int optimizationMode) { + if (mBackgroundUsageAllowabilityPreference == null + || mOptimizedPreference == null + || mUnrestrictedPreference == null) { + return; + } + final boolean isAllowBackground = optimizationMode != BatteryOptimizeUtils.MODE_RESTRICTED; + mBackgroundUsageAllowabilityPreference.setChecked(isAllowBackground); + mOptimizedPreference.setEnabled(isAllowBackground); + mUnrestrictedPreference.setEnabled(isAllowBackground); + mOptimizedPreference.setChecked(optimizationMode == BatteryOptimizeUtils.MODE_OPTIMIZED); + mUnrestrictedPreference.setChecked( + optimizationMode == BatteryOptimizeUtils.MODE_UNRESTRICTED); + } + + @VisibleForTesting + void handleBatteryOptimizeModeUpdated(int newBatteryOptimizeMode) { + if (mBatteryOptimizeUtils.getAppOptimizationMode() == newBatteryOptimizeMode) { + Log.w(TAG, "ignore same mode for: " + mBatteryOptimizeUtils.getPackageName()); + return; + } + mBatteryOptimizeUtils.setAppUsageState( + newBatteryOptimizeMode, BatteryOptimizeHistoricalLogEntry.Action.APPLY); + updatePreferences(newBatteryOptimizeMode); + } +} diff --git a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java b/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java deleted file mode 100644 index a26ab7a58e4..00000000000 --- a/src/com/android/settings/fuelgauge/OptimizedPreferenceController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -public class OptimizedPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "OPTIMIZED_PREF"; - - @VisibleForTesting static final String KEY_OPTIMIZED_PREF = "optimized_preference"; - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public OptimizedPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public void updateState(Preference preference) { - preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - - final boolean isOptimized = - mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly() - || mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_OPTIMIZED; - ((SelectorWithWidgetPreference) preference).setChecked(isOptimized); - } - - @Override - public String getPreferenceKey() { - return KEY_OPTIMIZED_PREF; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java index e59cc4add46..dadf2e89fbe 100644 --- a/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java +++ b/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetail.java @@ -24,11 +24,8 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.UserHandle; -import android.text.TextUtils; import android.util.Log; import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; import androidx.annotation.VisibleForTesting; @@ -44,8 +41,6 @@ import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.MainSwitchPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import java.util.ArrayList; import java.util.List; @@ -53,8 +48,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** Allow background usage fragment for each app */ -public class PowerBackgroundUsageDetail extends DashboardFragment - implements SelectorWithWidgetPreference.OnClickListener, OnCheckedChangeListener { +public class PowerBackgroundUsageDetail extends DashboardFragment { private static final String TAG = "PowerBackgroundUsageDetail"; public static final String EXTRA_UID = "extra_uid"; @@ -63,21 +57,15 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public static final String EXTRA_POWER_USAGE_AMOUNT = "extra_power_usage_amount"; public static final String EXTRA_ICON_ID = "extra_icon_id"; private static final String KEY_PREF_HEADER = "header_view"; - private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; - private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; + private static final String KEY_BATTERY_OPTIMIZATION_MODE_CATEGORY = + "battery_optimization_mode_category"; private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); - @VisibleForTesting LayoutPreference mHeaderPreference; @VisibleForTesting ApplicationsState mState; @VisibleForTesting ApplicationsState.AppEntry mAppEntry; @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - @VisibleForTesting SelectorWithWidgetPreference mOptimizePreference; - @VisibleForTesting SelectorWithWidgetPreference mUnrestrictedPreference; - @VisibleForTesting MainSwitchPreference mMainSwitchPreference; - @VisibleForTesting FooterPreference mFooterPreference; @VisibleForTesting StringBuilder mLogStringBuilder; @VisibleForTesting @BatteryOptimizeUtils.OptimizationMode @@ -87,17 +75,11 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public void onAttach(Activity activity) { super.onAttach(activity); + final Bundle bundle = getArguments(); + final int uid = bundle.getInt(EXTRA_UID, 0); + final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); + mBatteryOptimizeUtils = new BatteryOptimizeUtils(getContext(), uid, packageName); mState = ApplicationsState.getInstance(getActivity().getApplication()); - } - - @Override - public void onCreate(Bundle icicle) { - super.onCreate(icicle); - - final String packageName = getArguments().getString(EXTRA_PACKAGE_NAME); - onCreateBackgroundUsageState(packageName); - mHeaderPreference = findPreference(KEY_PREF_HEADER); - if (packageName != null) { mAppEntry = mState.getEntry(packageName, UserHandle.myUserId()); } @@ -107,8 +89,8 @@ public class PowerBackgroundUsageDetail extends DashboardFragment public void onResume() { super.onResume(); initHeader(); - mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); initFooter(); + mOptimizationMode = mBatteryOptimizeUtils.getAppOptimizationMode(); mLogStringBuilder = new StringBuilder("onResume mode = ").append(mOptimizationMode); } @@ -136,20 +118,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment Log.d(TAG, "Leave with mode: " + currentOptimizeMode); } - @Override - public void onRadioButtonClicked(SelectorWithWidgetPreference selected) { - final String selectedKey = selected == null ? null : selected.getKey(); - updateSelectorPreferenceState(mUnrestrictedPreference, selectedKey); - updateSelectorPreferenceState(mOptimizePreference, selectedKey); - mBatteryOptimizeUtils.setAppUsageState(getSelectedPreference(), Action.APPLY); - } - - @Override - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - mMainSwitchPreference.setChecked(isChecked); - updateSelectorPreference(isChecked); - } - @Override public int getMetricsCategory() { return SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND; @@ -157,14 +125,10 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @Override protected List createPreferenceControllers(Context context) { - final List controllers = new ArrayList<>(); - final Bundle bundle = getArguments(); - final int uid = bundle.getInt(EXTRA_UID, 0); - final String packageName = bundle.getString(EXTRA_PACKAGE_NAME); - - controllers.add(new AllowBackgroundPreferenceController(context, uid, packageName)); - controllers.add(new OptimizedPreferenceController(context, uid, packageName)); - controllers.add(new UnrestrictedPreferenceController(context, uid, packageName)); + final List controllers = new ArrayList<>(1); + controllers.add( + new BatteryOptimizationModePreferenceController( + context, KEY_BATTERY_OPTIMIZATION_MODE_CATEGORY, mBatteryOptimizeUtils)); return controllers; } @@ -179,26 +143,6 @@ public class PowerBackgroundUsageDetail extends DashboardFragment return TAG; } - @VisibleForTesting - void updateSelectorPreference(boolean isEnabled) { - mOptimizePreference.setEnabled(isEnabled); - mUnrestrictedPreference.setEnabled(isEnabled); - onRadioButtonClicked(isEnabled ? mOptimizePreference : null); - } - - @VisibleForTesting - int getSelectedPreference() { - if (!mMainSwitchPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_RESTRICTED; - } else if (mUnrestrictedPreference.isChecked()) { - return BatteryOptimizeUtils.MODE_UNRESTRICTED; - } else if (mOptimizePreference.isChecked()) { - return BatteryOptimizeUtils.MODE_OPTIMIZED; - } else { - return BatteryOptimizeUtils.MODE_UNKNOWN; - } - } - static void startPowerBackgroundUsageDetailPage(Context context, Bundle args) { new SubSettingLauncher(context) .setDestination(PowerBackgroundUsageDetail.class.getName()) @@ -209,7 +153,11 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @VisibleForTesting void initHeader() { - final View appSnippet = mHeaderPreference.findViewById(R.id.entity_header); + final LayoutPreference headerPreference = findPreference(KEY_PREF_HEADER); + if (headerPreference == null) { + return; + } + final View appSnippet = headerPreference.findViewById(R.id.entity_header); final Activity context = getActivity(); final Bundle bundle = getArguments(); EntityHeaderController controller = @@ -239,58 +187,25 @@ public class PowerBackgroundUsageDetail extends DashboardFragment @VisibleForTesting void initFooter() { - final String stateString; - final String footerString; - final Context context = getContext(); - - if (mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()) { - // Present optimized only string when the package name is invalid. - stateString = context.getString(R.string.manager_battery_usage_optimized_only); - footerString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else if (mBatteryOptimizeUtils.isSystemOrDefaultApp()) { - // Present unrestricted only string when the package is system or default active app. - stateString = context.getString(R.string.manager_battery_usage_unrestricted_only); - footerString = - context.getString(R.string.manager_battery_usage_footer_limited, stateString); - } else { - // Present default string to normal app. - footerString = context.getString(R.string.manager_battery_usage_footer); + final FooterPreference footerPreference = findPreference(KEY_FOOTER_PREFERENCE); + if (footerPreference == null) { + return; } - mFooterPreference.setTitle(footerString); + final Context context = getContext(); + footerPreference.setTitle(context.getString(R.string.manager_battery_usage_footer)); final Intent helpIntent = HelpUtils.getHelpIntent( context, context.getString(R.string.help_url_app_usage_settings), /* backupContext= */ ""); if (helpIntent != null) { - mFooterPreference.setLearnMoreAction( + footerPreference.setLearnMoreAction( v -> startActivityForResult(helpIntent, /* requestCode= */ 0)); - mFooterPreference.setLearnMoreText( + footerPreference.setLearnMoreText( context.getString(R.string.manager_battery_usage_link_a11y)); } } - private void onCreateBackgroundUsageState(String packageName) { - mOptimizePreference = findPreference(KEY_PREF_OPTIMIZED); - mUnrestrictedPreference = findPreference(KEY_PREF_UNRESTRICTED); - mMainSwitchPreference = findPreference(KEY_ALLOW_BACKGROUND_USAGE); - mFooterPreference = findPreference(KEY_FOOTER_PREFERENCE); - - mOptimizePreference.setOnClickListener(this); - mUnrestrictedPreference.setOnClickListener(this); - mMainSwitchPreference.addOnSwitchChangeListener(this); - - mBatteryOptimizeUtils = - new BatteryOptimizeUtils( - getContext(), getArguments().getInt(EXTRA_UID), packageName); - } - - private void updateSelectorPreferenceState( - SelectorWithWidgetPreference preference, String selectedKey) { - preference.setChecked(TextUtils.equals(selectedKey, preference.getKey())); - } - private void logMetricCategory(int currentOptimizeMode) { if (currentOptimizeMode == mOptimizationMode) { return; diff --git a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java b/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java deleted file mode 100644 index 652941b2544..00000000000 --- a/src/com/android/settings/fuelgauge/UnrestrictedPreferenceController.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import android.content.Context; - -import androidx.annotation.VisibleForTesting; -import androidx.preference.Preference; - -import com.android.settings.core.PreferenceControllerMixin; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -public class UnrestrictedPreferenceController extends AbstractPreferenceController - implements PreferenceControllerMixin { - - private static final String TAG = "UNRESTRICTED_PREF"; - - @VisibleForTesting static final String KEY_UNRESTRICTED_PREF = "unrestricted_preference"; - - @VisibleForTesting BatteryOptimizeUtils mBatteryOptimizeUtils; - - public UnrestrictedPreferenceController(Context context, int uid, String packageName) { - super(context); - mBatteryOptimizeUtils = new BatteryOptimizeUtils(context, uid, packageName); - } - - @Override - public void updateState(Preference preference) { - preference.setEnabled(mBatteryOptimizeUtils.isSelectorPreferenceEnabled()); - - final boolean isUnrestricted = - mBatteryOptimizeUtils.getAppOptimizationMode() - == BatteryOptimizeUtils.MODE_UNRESTRICTED; - ((SelectorWithWidgetPreference) preference).setChecked(isUnrestricted); - } - - @Override - public boolean isAvailable() { - return true; - } - - @Override - public String getPreferenceKey() { - return KEY_UNRESTRICTED_PREF; - } - - @Override - public boolean handlePreferenceTreeClick(Preference preference) { - return getPreferenceKey().equals(preference.getKey()); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index 1bc00a1fdff..f3848b3fbb2 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -17,10 +17,10 @@ package com.android.settings.fuelgauge; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; -import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; @@ -49,13 +49,13 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.SettingsActivity; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import com.android.settings.fuelgauge.batteryusage.BatteryDiffEntry; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.fuelgauge.batteryusage.ConvertUtils; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.widget.EntityHeaderController; -import com.android.settingslib.PrimarySwitchPreference; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.applications.instantapps.InstantAppDataProvider; @@ -87,12 +87,12 @@ import java.util.concurrent.TimeUnit; }) public class AdvancedPowerUsageDetailTest { - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String APP_LABEL = "app label"; private static final String SUMMARY = "summary"; - private static final String[] PACKAGE_NAME = {"com.android.app"}; + private static final String PACKAGE_NAME = "com.android.app"; + private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; private static final String USAGE_PERCENT = "16%"; private static final int ICON_ID = 123; private static final int UID = 1; @@ -100,39 +100,43 @@ public class AdvancedPowerUsageDetailTest { private static final long FOREGROUND_SERVICE_TIME_MS = 123; private static final long BACKGROUND_TIME_MS = 100; private static final long SCREEN_ON_TIME_MS = 321; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private FragmentActivity mActivity; @Mock private EntityHeaderController mEntityHeaderController; - @Mock private LayoutPreference mHeaderPreference; @Mock private ApplicationsState mState; @Mock private ApplicationsState.AppEntry mAppEntry; @Mock private BatteryEntry mBatteryEntry; @Mock private PackageManager mPackageManager; @Mock private InstallSourceInfo mInstallSourceInfo; + @Mock private LayoutPreference mLayoutPreference; @Mock private AppOpsManager mAppOpsManager; @Mock private LoaderManager mLoaderManager; - @Mock private BatteryOptimizeUtils mBatteryOptimizeUtils; + private int mTestMode; private Context mContext; - private PrimarySwitchPreference mAllowBackgroundUsagePreference; private AdvancedPowerUsageDetail mFragment; private SettingsActivity mTestActivity; private FakeFeatureFactory mFeatureFactory; private MetricsFeatureProvider mMetricsFeatureProvider; private BatteryDiffEntry mBatteryDiffEntry; private Bundle mBundle; + private BatteryOptimizeUtils mBatteryOptimizeUtils; @Before - public void setUp() { + public void setUp() throws Exception { mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getPackageName()).thenReturn("foo"); + when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; + prepareTestBatteryOptimizationUtils(); mFragment = spy(new AdvancedPowerUsageDetail()); + mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; + doReturn(mLayoutPreference).when(mFragment).findPreference(any()); mBundle = spy(new Bundle()); doReturn(mContext).when(mFragment).getContext(); doReturn(mActivity).when(mFragment).getActivity(); @@ -195,7 +199,6 @@ public class AdvancedPowerUsageDetailTest { when(mBatteryDiffEntry.getAppLabel()).thenReturn(APP_LABEL); when(mBatteryDiffEntry.getAppIconId()).thenReturn(ICON_ID); - mFragment.mHeaderPreference = mHeaderPreference; mFragment.mState = mState; mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mFragment.mLogStringBuilder = new StringBuilder(); @@ -219,10 +222,6 @@ public class AdvancedPowerUsageDetailTest { .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); doAnswer(callable).when(mActivity).startActivity(captor.capture()); doAnswer(callable).when(mContext).startActivity(captor.capture()); - - mAllowBackgroundUsagePreference = new PrimarySwitchPreference(mContext); - mAllowBackgroundUsagePreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); - mFragment.mAllowBackgroundUsagePreference = mAllowBackgroundUsagePreference; } @After @@ -320,17 +319,15 @@ public class AdvancedPowerUsageDetailTest { .isEqualTo(SCREEN_ON_TIME_MS); assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_POWER_USAGE_PERCENT)) .isEqualTo(USAGE_PERCENT); - assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME)) - .isEqualTo(null); + assertThat(mBundle.getString(AdvancedPowerUsageDetail.EXTRA_SLOT_TIME)).isNull(); } - @Test public void startBatteryDetailPage_noBatteryUsage_hasBasicData() { final ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); AdvancedPowerUsageDetail.startBatteryDetailPage( - mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER); + mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); verify(mActivity).startActivity(captor.capture()); @@ -338,7 +335,7 @@ public class AdvancedPowerUsageDetailTest { captor.getValue() .getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS) .getString(AdvancedPowerUsageDetail.EXTRA_PACKAGE_NAME)) - .isEqualTo(PACKAGE_NAME[0]); + .isEqualTo(PACKAGE_NAME); assertThat( captor.getValue() @@ -351,62 +348,21 @@ public class AdvancedPowerUsageDetailTest { public void startBatteryDetailPage_batteryEntryNotExisted_extractUidFromPackageName() throws PackageManager.NameNotFoundException { mBundle.clear(); - doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME[0], 0 /* no flag */); + doReturn(UID).when(mPackageManager).getPackageUid(PACKAGE_NAME, 0 /* no flag */); AdvancedPowerUsageDetail.startBatteryDetailPage( - mActivity, mFragment, PACKAGE_NAME[0], UserHandle.OWNER); + mActivity, mFragment, PACKAGE_NAME, UserHandle.OWNER); assertThat(mBundle.getInt(AdvancedPowerUsageDetail.EXTRA_UID)).isEqualTo(UID); } @Test - public void initFooter_isValidPackageName_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); + public void onPause_optimizationModeIsChanged_logPreference() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("This app requires optimized battery usage."); - } - - @Test - public void initFooter_isSystemOrDefaultApp_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("This app requires unrestricted battery usage."); - } - - @Test - public void initFooter_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mFragment.initFooter(); - - assertThat(mAllowBackgroundUsagePreference.getSummary().toString()) - .isEqualTo("Enable for real-time updates, disable to save battery"); - } - - @Test - public void onPause_optimizationModeChanged_logPreference() - throws PackageManager.NameNotFoundException, InterruptedException { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); @@ -415,27 +371,18 @@ public class AdvancedPowerUsageDetailTest { SettingsEnums.LEAVE_APP_BATTERY_USAGE, SettingsEnums.ACTION_APP_BATTERY_USAGE_ALLOW_BACKGROUND, SettingsEnums.FUELGAUGE_POWER_USAGE_DETAIL, - packageName, + PACKAGE_NAME, /* consumed battery */ 0); } @Test - public void onPause_optimizationModeIsNotChanged_notInvokeLogging() - throws PackageManager.NameNotFoundException, InterruptedException { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); - when(mContext.getPackageManager()).thenReturn(mPackageManager); - when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); + public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_RESTRICTED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); - mFragment.onPreferenceChange(mAllowBackgroundUsagePreference, false); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); + mBatteryOptimizeUtils.setAppUsageState(BatteryOptimizeUtils.MODE_RESTRICTED, Action.APPLY); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); @@ -446,4 +393,16 @@ public class AdvancedPowerUsageDetailTest { public void shouldSkipForInitialSUW_returnTrue() { assertThat(mFragment.shouldSkipForInitialSUW()).isTrue(); } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java deleted file mode 100644 index 261a3151531..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/AllowBackgroundPreferenceControllerTest.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.pm.PackageManager; - -import com.android.settingslib.widget.MainSwitchPreference; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class AllowBackgroundPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private AllowBackgroundPreferenceController mController; - private MainSwitchPreference mMainSwitchPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock private PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new AllowBackgroundPreferenceController(context, UID, PACKAGE_NAME); - mMainSwitchPreference = new MainSwitchPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_isValidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mMainSwitchPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - } - - @Test - public void testUpdateState_invalidPackage_prefDisabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mMainSwitchPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndRestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - assertThat(mMainSwitchPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isFalse(); - assertThat(mMainSwitchPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isRestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_RESTRICTED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - assertThat(mMainSwitchPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mMainSwitchPreference); - - assertThat(mMainSwitchPreference.isEnabled()).isTrue(); - assertThat(mMainSwitchPreference.isChecked()).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mMainSwitchPreference.setKey( - AllowBackgroundPreferenceController.KEY_ALLOW_BACKGROUND_USAGE); - mController.handlePreferenceTreeClick(mMainSwitchPreference); - - assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mMainSwitchPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java new file mode 100644 index 00000000000..190446ed116 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BackgroundUsageAllowabilityPreferenceControllerTest.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settings.R; +import com.android.settings.dashboard.DashboardFragment; +import com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; +import com.android.settingslib.PrimarySwitchPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BackgroundUsageAllowabilityPreferenceControllerTest { + private static final int UID = 12345; + private static final String PACKAGE_NAME = "com.android.app"; + + private int mTestMode; + private Context mContext; + private BackgroundUsageAllowabilityPreferenceController mBackgroundUsageController; + private BatteryOptimizeUtils mBatteryOptimizeUtils; + + @Mock DashboardFragment mDashboardFragment; + @Mock PrimarySwitchPreference mBackgroundUsageAllowabilityPreference; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + prepareTestBatteryOptimizationUtils(); + mBackgroundUsageController = + spy( + new BackgroundUsageAllowabilityPreferenceController( + mContext, + mDashboardFragment, + /* preferenceKey= */ "test", + mBatteryOptimizeUtils)); + mBackgroundUsageController.mBackgroundUsageAllowabilityPreference = + mBackgroundUsageAllowabilityPreference; + } + + @Test + public void initPreferences_immutableOptimized_setExpectedContent() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(true).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(false); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_footer_limited, + mContext.getString(R.string.manager_battery_usage_optimized_only))); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_immutableUnrestricted_setExpectedContent() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(false).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + doReturn(true).when(mBatteryOptimizeUtils).isSystemOrDefaultApp(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(false); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_footer_limited, + mContext.getString( + R.string.manager_battery_usage_unrestricted_only))); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_mutableMode_setExpectedContent() { + doReturn(true).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + doReturn(false).when(mBatteryOptimizeUtils).isDisabledForOptimizeModeOnly(); + doReturn(false).when(mBatteryOptimizeUtils).isSystemOrDefaultApp(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(true); + verify(mBackgroundUsageAllowabilityPreference).setSwitchEnabled(true); + verify(mBackgroundUsageAllowabilityPreference) + .setSummary( + mContext.getString( + R.string.manager_battery_usage_allow_background_usage_summary)); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceChangeListener(any()); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceClickListener(any()); + } + + @Test + public void updatePreferences_setIntoUnrestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoOptimizedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoRestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + verify(mBatteryOptimizeUtils) + .setAppUsageState(BatteryOptimizeUtils.MODE_OPTIMIZED, Action.APPLY); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeNotChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_RESTRICTED); + + verify(mBatteryOptimizeUtils, never()).setAppUsageState(anyInt(), any()); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_RESTRICTED); + verify(mBackgroundUsageController, never()).updatePreferences(mTestMode); + } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } + + private void verifyPreferences(int mode) { + boolean isAllowBackground = mode != BatteryOptimizeUtils.MODE_RESTRICTED; + verify(mBackgroundUsageAllowabilityPreference).setChecked(isAllowBackground); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java new file mode 100644 index 00000000000..2ddc7ebbc72 --- /dev/null +++ b/tests/robotests/src/com/android/settings/fuelgauge/BatteryOptimizationModePreferenceControllerTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.fuelgauge; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.content.Context; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.settingslib.widget.MainSwitchPreference; +import com.android.settingslib.widget.SelectorWithWidgetPreference; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class BatteryOptimizationModePreferenceControllerTest { + private static final int UID = 12345; + private static final String PACKAGE_NAME = "com.android.app"; + + private int mTestMode; + private Context mContext; + private BatteryOptimizationModePreferenceController mBackgroundUsageController; + private BatteryOptimizeUtils mBatteryOptimizeUtils; + + @Mock MainSwitchPreference mBackgroundUsageAllowabilityPreference; + @Mock SelectorWithWidgetPreference mOptimizedPreference; + @Mock SelectorWithWidgetPreference mUnrestrictedPreference; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + mContext = spy(ApplicationProvider.getApplicationContext()); + prepareTestBatteryOptimizationUtils(); + mBackgroundUsageController = + spy( + new BatteryOptimizationModePreferenceController( + mContext, "test", mBatteryOptimizeUtils)); + mBackgroundUsageController.mBackgroundUsageAllowabilityPreference = + mBackgroundUsageAllowabilityPreference; + mBackgroundUsageController.mOptimizedPreference = mOptimizedPreference; + mBackgroundUsageController.mUnrestrictedPreference = mUnrestrictedPreference; + } + + @Test + public void initPreferences_mutableMode_setEnabled() { + doReturn(true).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(true); + verify(mOptimizedPreference).setEnabled(true); + verify(mUnrestrictedPreference).setEnabled(true); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + verify(mBackgroundUsageAllowabilityPreference).setOnPreferenceChangeListener(any()); + verify(mOptimizedPreference).setOnPreferenceClickListener(any()); + verify(mUnrestrictedPreference).setOnPreferenceClickListener(any()); + } + + @Test + public void initPreferences_immutableMode_setDisabledAndSkipSetListeners() { + doReturn(false).when(mBatteryOptimizeUtils).isOptimizeModeMutable(); + + mBackgroundUsageController.initPreferences(); + + verify(mBackgroundUsageAllowabilityPreference).setEnabled(false); + verify(mOptimizedPreference).setEnabled(false); + verify(mUnrestrictedPreference).setEnabled(false); + verify(mBackgroundUsageAllowabilityPreference, never()).setOnPreferenceClickListener(any()); + verify(mBackgroundUsageAllowabilityPreference, never()) + .setOnPreferenceChangeListener(any()); + verify(mOptimizedPreference, never()).setOnPreferenceClickListener(any()); + verify(mUnrestrictedPreference, never()).setOnPreferenceClickListener(any()); + } + + @Test + public void updatePreferences_setIntoUnrestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoOptimizedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void updatePreferences_setIntoRestrictedMode_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.updatePreferences(mTestMode); + + verifyPreferences(mTestMode); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_OPTIMIZED); + + verify(mBatteryOptimizeUtils) + .setAppUsageState( + BatteryOptimizeUtils.MODE_OPTIMIZED, + BatteryOptimizeHistoricalLogEntry.Action.APPLY); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); + verifyPreferences(mBatteryOptimizeUtils.getAppOptimizationMode()); + } + + @Test + public void handleBatteryOptimizeModeUpdated_modeNotChange_setExpectedPrefStatus() { + mTestMode = BatteryOptimizeUtils.MODE_RESTRICTED; + + mBackgroundUsageController.handleBatteryOptimizeModeUpdated( + BatteryOptimizeUtils.MODE_RESTRICTED); + + verify(mBatteryOptimizeUtils, never()).setAppUsageState(anyInt(), any()); + assertThat(mTestMode).isEqualTo(BatteryOptimizeUtils.MODE_RESTRICTED); + verify(mBackgroundUsageController, never()).updatePreferences(anyInt()); + } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } + + private void verifyPreferences(int mode) { + boolean isAllowBackground = mode != BatteryOptimizeUtils.MODE_RESTRICTED; + verify(mBackgroundUsageAllowabilityPreference).setChecked(isAllowBackground); + verify(mOptimizedPreference).setEnabled(isAllowBackground); + verify(mUnrestrictedPreference).setEnabled(isAllowBackground); + verify(mOptimizedPreference).setChecked(mode == BatteryOptimizeUtils.MODE_OPTIMIZED); + verify(mUnrestrictedPreference).setChecked(mode == BatteryOptimizeUtils.MODE_UNRESTRICTED); + } +} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java deleted file mode 100644 index 66564718d0e..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/OptimizedPreferenceControllerTest.java +++ /dev/null @@ -1,130 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.pm.PackageManager; - -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class OptimizedPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private OptimizedPreferenceController mController; - private SelectorWithWidgetPreference mPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new OptimizedPreferenceController(context, UID, PACKAGE_NAME); - mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_invalidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndOptimizeStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isOptimizedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mPreference.setKey(mController.KEY_OPTIMIZED_PREF); - mController.handlePreferenceTreeClick(mPreference); - - assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); - } -} diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java index 90611178fdb..9f98d78541e 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java @@ -17,10 +17,10 @@ package com.android.settings.fuelgauge; import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS; -import static com.android.settings.fuelgauge.BatteryOptimizeHistoricalLogEntry.Action; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; @@ -42,16 +42,17 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.UserHandle; -import android.widget.CompoundButton; import androidx.fragment.app.FragmentActivity; import androidx.loader.app.LoaderManager; import androidx.test.core.app.ApplicationProvider; +import com.android.settings.R; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; +import com.android.settings.testutils.shadow.ShadowHelpUtils; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; import com.android.settingslib.applications.ApplicationsState; @@ -59,8 +60,6 @@ import com.android.settingslib.applications.instantapps.InstantAppDataProvider; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.FooterPreference; import com.android.settingslib.widget.LayoutPreference; -import com.android.settingslib.widget.MainSwitchPreference; -import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.After; import org.junit.Before; @@ -83,36 +82,33 @@ import java.util.concurrent.TimeUnit; @Config( shadows = { ShadowEntityHeaderController.class, + ShadowHelpUtils.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class PowerBackgroundUsageDetailTest { - @Rule - public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private static final String APP_LABEL = "app label"; private static final String SUMMARY = "summary"; private static final int ICON_ID = 123; private static final int UID = 1; - private static final String KEY_PREF_UNRESTRICTED = "unrestricted_preference"; - private static final String KEY_PREF_OPTIMIZED = "optimized_preference"; - private static final String KEY_ALLOW_BACKGROUND_USAGE = "allow_background_usage"; + private static final String PACKAGE_NAME = "com.android.app"; + private static final String KEY_PREF_HEADER = "header_view"; + private static final String KEY_FOOTER_PREFERENCE = "app_usage_footer_preference"; + private static final String INITIATING_PACKAGE_NAME = "com.android.vending"; + private int mTestMode; private Context mContext; private PowerBackgroundUsageDetail mFragment; - private FooterPreference mFooterPreference; - private MainSwitchPreference mMainSwitchPreference; private MetricsFeatureProvider mMetricsFeatureProvider; - private SelectorWithWidgetPreference mOptimizePreference; - private SelectorWithWidgetPreference mUnrestrictedPreference; private SettingsActivity mTestActivity; + private BatteryOptimizeUtils mBatteryOptimizeUtils; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private FragmentActivity mActivity; @Mock private EntityHeaderController mEntityHeaderController; - @Mock private BatteryOptimizeUtils mBatteryOptimizeUtils; - @Mock private LayoutPreference mHeaderPreference; @Mock private ApplicationsState mState; @Mock private Bundle mBundle; @Mock private LoaderManager mLoaderManager; @@ -120,21 +116,26 @@ public class PowerBackgroundUsageDetailTest { @Mock private BatteryEntry mBatteryEntry; @Mock private PackageManager mPackageManager; @Mock private AppOpsManager mAppOpsManager; - @Mock private CompoundButton mMockSwitch; @Mock private InstallSourceInfo mInstallSourceInfo; + @Mock private LayoutPreference mLayoutPreference; + @Mock private FooterPreference mFooterPreference; @Before public void setUp() throws Exception { mContext = spy(ApplicationProvider.getApplicationContext()); - when(mContext.getPackageName()).thenReturn("foo"); + when(mContext.getPackageName()).thenReturn(PACKAGE_NAME); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); final FakeFeatureFactory fakeFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = fakeFeatureFactory.metricsFeatureProvider; + prepareTestBatteryOptimizationUtils(); mFragment = spy(new PowerBackgroundUsageDetail()); + mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mFragment.mLogStringBuilder = new StringBuilder(); + doReturn(mLayoutPreference).when(mFragment).findPreference(KEY_PREF_HEADER); + doReturn(mFooterPreference).when(mFragment).findPreference(KEY_FOOTER_PREFERENCE); doReturn(mContext).when(mFragment).getContext(); doReturn(mActivity).when(mFragment).getActivity(); doReturn(SUMMARY).when(mFragment).getString(anyInt()); @@ -169,9 +170,7 @@ public class PowerBackgroundUsageDetailTest { when(mBatteryEntry.getLabel()).thenReturn(APP_LABEL); mBatteryEntry.mIconId = ICON_ID; - mFragment.mHeaderPreference = mHeaderPreference; mFragment.mState = mState; - mFragment.mBatteryOptimizeUtils = mBatteryOptimizeUtils; mAppEntry.info = mock(ApplicationInfo.class); mTestActivity = spy(new SettingsActivity()); @@ -191,23 +190,12 @@ public class PowerBackgroundUsageDetailTest { .when(mActivity) .startActivityAsUser(captor.capture(), nullable(UserHandle.class)); doAnswer(callable).when(mActivity).startActivity(captor.capture()); - - mFooterPreference = spy(new FooterPreference(mContext)); - mMainSwitchPreference = spy(new MainSwitchPreference(mContext)); - mMainSwitchPreference.setKey(KEY_ALLOW_BACKGROUND_USAGE); - mOptimizePreference = spy(new SelectorWithWidgetPreference(mContext)); - mOptimizePreference.setKey(KEY_PREF_OPTIMIZED); - mUnrestrictedPreference = spy(new SelectorWithWidgetPreference(mContext)); - mUnrestrictedPreference.setKey(KEY_PREF_UNRESTRICTED); - mFragment.mFooterPreference = mFooterPreference; - mFragment.mMainSwitchPreference = mMainSwitchPreference; - mFragment.mOptimizePreference = mOptimizePreference; - mFragment.mUnrestrictedPreference = mUnrestrictedPreference; } @After public void reset() { ShadowEntityHeaderController.reset(); + ShadowHelpUtils.reset(); } @Test @@ -258,91 +246,64 @@ public class PowerBackgroundUsageDetailTest { } @Test - public void initFooter_hasCorrectString() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - + public void initFooter_setExpectedFooterContent() { mFragment.initFooter(); - assertThat(mFooterPreference.getTitle().toString()) - .isEqualTo("Changing how an app uses your battery can affect its performance."); + verify(mFooterPreference) + .setTitle(mContext.getString(R.string.manager_battery_usage_footer)); + verify(mFooterPreference).setLearnMoreAction(any()); + verify(mFooterPreference) + .setLearnMoreText(mContext.getString(R.string.manager_battery_usage_link_a11y)); } @Test - public void onSwitchChanged_fromUnrestrictedModeSetDisabled_becomeRestrictedMode() { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = optimizedMode; + public void onPause_optimizationModeIsChanged_logPreference() throws Exception { + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ false); - - verify(mOptimizePreference).setEnabled(false); - verify(mUnrestrictedPreference).setEnabled(false); - verify(mFragment).onRadioButtonClicked(null); - verify(mMainSwitchPreference).setChecked(false); - assertThat(mFragment.getSelectedPreference()).isEqualTo(restrictedMode); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - } - - @Test - public void onSwitchChanged_fromRestrictedModeSetEnabled_becomeOptimizedMode() { - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - - verify(mOptimizePreference).setEnabled(true); - verify(mUnrestrictedPreference).setEnabled(true); - verify(mFragment).onRadioButtonClicked(mOptimizePreference); - verify(mMainSwitchPreference).setChecked(true); - verify(mOptimizePreference).setChecked(true); - assertThat(mFragment.getSelectedPreference()).isEqualTo(optimizedMode); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - } - - @Test - public void onPause_optimizationModeChanged_logPreference() throws Exception { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); - - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); verify(mMetricsFeatureProvider) .action( SettingsEnums.LEAVE_POWER_USAGE_MANAGE_BACKGROUND, - SettingsEnums.ACTION_APP_BATTERY_USAGE_OPTIMIZED, + SettingsEnums.ACTION_APP_BATTERY_USAGE_UNRESTRICTED, SettingsEnums.FUELGAUGE_POWER_USAGE_MANAGE_BACKGROUND, - packageName, + PACKAGE_NAME, /* consumed battery */ 0); } @Test public void onPause_optimizationModeIsNotChanged_notInvokeLogging() throws Exception { - final String packageName = "testPackageName"; - final int restrictedMode = BatteryOptimizeUtils.MODE_RESTRICTED; - final int optimizedMode = BatteryOptimizeUtils.MODE_OPTIMIZED; - mFragment.mOptimizationMode = restrictedMode; - when(mBatteryOptimizeUtils.getPackageName()).thenReturn(packageName); - when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn("com.android.vending"); + mFragment.mOptimizationMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + when(mBatteryOptimizeUtils.getPackageName()).thenReturn(PACKAGE_NAME); + when(mInstallSourceInfo.getInitiatingPackageName()).thenReturn(INITIATING_PACKAGE_NAME); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ true); - verify(mBatteryOptimizeUtils).setAppUsageState(optimizedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(optimizedMode); - mFragment.onCheckedChanged(mMockSwitch, /* isChecked= */ false); - verify(mBatteryOptimizeUtils).setAppUsageState(restrictedMode, Action.APPLY); - when(mBatteryOptimizeUtils.getAppOptimizationMode()).thenReturn(restrictedMode); + mTestMode = BatteryOptimizeUtils.MODE_UNRESTRICTED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_UNRESTRICTED); + mTestMode = BatteryOptimizeUtils.MODE_OPTIMIZED; + assertThat(mBatteryOptimizeUtils.getAppOptimizationMode()) + .isEqualTo(BatteryOptimizeUtils.MODE_OPTIMIZED); mFragment.onPause(); TimeUnit.SECONDS.sleep(1); verifyNoInteractions(mMetricsFeatureProvider); } + + private void prepareTestBatteryOptimizationUtils() { + mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(mContext, UID, PACKAGE_NAME)); + Answer setTestMode = + invocation -> { + mTestMode = invocation.getArgument(0); + return null; + }; + doAnswer(setTestMode).when(mBatteryOptimizeUtils).setAppUsageState(anyInt(), any()); + Answer getTestMode = invocation -> mTestMode; + doAnswer(getTestMode).when(mBatteryOptimizeUtils).getAppOptimizationMode(); + } } diff --git a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java deleted file mode 100644 index 0c6f7da2109..00000000000 --- a/tests/robotests/src/com/android/settings/fuelgauge/UnrestrictedPreferenceControllerTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.fuelgauge; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.content.pm.PackageManager; - -import com.android.settingslib.widget.SelectorWithWidgetPreference; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; - -@RunWith(RobolectricTestRunner.class) -public class UnrestrictedPreferenceControllerTest { - private static final int UID = 12345; - private static final String PACKAGE_NAME = "com.android.app"; - - private UnrestrictedPreferenceController mController; - private SelectorWithWidgetPreference mPreference; - private BatteryOptimizeUtils mBatteryOptimizeUtils; - - @Mock PackageManager mMockPackageManager; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - Context context = spy(RuntimeEnvironment.application); - BatteryUtils.getInstance(context).reset(); - doReturn(UID) - .when(mMockPackageManager) - .getPackageUid(PACKAGE_NAME, PackageManager.GET_META_DATA); - - mController = new UnrestrictedPreferenceController(context, UID, PACKAGE_NAME); - mPreference = new SelectorWithWidgetPreference(RuntimeEnvironment.application); - mBatteryOptimizeUtils = spy(new BatteryOptimizeUtils(context, UID, PACKAGE_NAME)); - mController.mBatteryOptimizeUtils = mBatteryOptimizeUtils; - } - - @Test - public void testUpdateState_isValidPackage_prefEnabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isTrue(); - assertThat(mPreference.isEnabled()).isTrue(); - } - - @Test - public void testUpdateState_invalidPackage_prefDisabled() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(true); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(false); - - mController.updateState(mPreference); - - assertThat(mBatteryOptimizeUtils.isOptimizeModeMutable()).isFalse(); - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void testUpdateState_isSystemOrDefaultAppAndUnrestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_isSystemOrDefaultApp_prefUnchecked() { - when(mBatteryOptimizeUtils.isDisabledForOptimizeModeOnly()).thenReturn(false); - when(mBatteryOptimizeUtils.isSystemOrDefaultApp()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testUpdateState_isUnrestrictedStates_prefChecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_UNRESTRICTED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void testUpdateState_prefUnchecked() { - when(mBatteryOptimizeUtils.isOptimizeModeMutable()).thenReturn(true); - when(mBatteryOptimizeUtils.getAppOptimizationMode()) - .thenReturn(BatteryOptimizeUtils.MODE_OPTIMIZED); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - assertThat(mPreference.isChecked()).isFalse(); - } - - @Test - public void testHandlePreferenceTreeClick_samePrefKey_verifyAction() { - mPreference.setKey(mController.KEY_UNRESTRICTED_PREF); - mController.handlePreferenceTreeClick(mPreference); - - assertThat(mController.handlePreferenceTreeClick(mPreference)).isTrue(); - } - - @Test - public void testHandlePreferenceTreeClick_incorrectPrefKey_noAction() { - assertThat(mController.handlePreferenceTreeClick(mPreference)).isFalse(); - } -} From 8189b40067ab4db04e7002035f0c1fbc753e50c3 Mon Sep 17 00:00:00 2001 From: Jason Chiu Date: Fri, 18 Oct 2024 19:13:15 +0800 Subject: [PATCH 7/7] Make NetworkProviderSettings extend RestrictedDashboardFragment RestrictedSettingsFragment is deprecated and no longer supported. To be compatible with catalyst, change the super class to RestrictedDashboardFragment. Bug: 372733639 Test: atest NetworkProviderSettingsTest, manual tests with user restrictions Flag: EXEMPT N/A Change-Id: I02b08363dadd5739a1d348f099d439b9f82ffb18 --- .../network/NetworkProviderSettings.java | 16 ++++++++++++---- .../network/NetworkProviderSettingsTest.java | 5 +++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java index 69183ff25c0..b268461a0ec 100644 --- a/src/com/android/settings/network/NetworkProviderSettings.java +++ b/src/com/android/settings/network/NetworkProviderSettings.java @@ -64,8 +64,8 @@ import androidx.recyclerview.widget.RecyclerView; import com.android.settings.AirplaneModeEnabler; import com.android.settings.R; -import com.android.settings.RestrictedSettingsFragment; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.dashboard.RestrictedDashboardFragment; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.location.WifiScanningFragment; @@ -104,7 +104,7 @@ import java.util.Optional; * UI for Mobile network and Wi-Fi network settings. */ @SearchIndexable -public class NetworkProviderSettings extends RestrictedSettingsFragment +public class NetworkProviderSettings extends RestrictedDashboardFragment implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback, WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener, AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener { @@ -356,9 +356,17 @@ public class NetworkProviderSettings extends RestrictedSettingsFragment mIsGuest = userManager.isGuestUser(); } - private void addPreferences() { - addPreferencesFromResource(R.xml.network_provider_settings); + @Override + protected String getLogTag() { + return TAG; + } + @Override + protected int getPreferenceScreenResId() { + return R.xml.network_provider_settings; + } + + private void addPreferences() { mAirplaneModeMsgPreference = findPreference(PREF_KEY_AIRPLANE_MODE_MSG); updateAirplaneModeMsgPreference(mAirplaneModeEnabler.isAirplaneModeOn() /* visible */); mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS); diff --git a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java index 400f73f7f56..df399d779e1 100644 --- a/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java +++ b/tests/robotests/src/com/android/settings/network/NetworkProviderSettingsTest.java @@ -70,6 +70,7 @@ import androidx.test.core.app.ApplicationProvider; import com.android.settings.AirplaneModeEnabler; import com.android.settings.R; +import com.android.settings.dashboard.DashboardFeatureProvider; import com.android.settings.datausage.DataUsagePreference; import com.android.settings.testutils.shadow.ShadowDataUsageUtils; import com.android.settings.testutils.shadow.ShadowFragment; @@ -98,6 +99,7 @@ import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadows.ShadowToast; +import org.robolectric.util.ReflectionHelpers; import java.util.List; @@ -190,6 +192,9 @@ public class NetworkProviderSettingsTest { .when(mFirstWifiEntryPreferenceCategory).getKey(); mNetworkProviderSettings.mFirstWifiEntryPreferenceCategory = mFirstWifiEntryPreferenceCategory; + + ReflectionHelpers.setField(mNetworkProviderSettings, "mDashboardFeatureProvider", + mock(DashboardFeatureProvider.class)); } @Test