From 6b1393a2d7a30241dded6fe63c24b83135b0f148 Mon Sep 17 00:00:00 2001 From: Haijie Hong Date: Mon, 18 Dec 2023 20:17:02 +0800 Subject: [PATCH 1/8] Fix AdvancedBluetoothDetailsHeaderControllerTest Bug: 313014781 Test: atest AdvancedBluetoothDetailsHeaderControllerTest Change-Id: I42cea3add9d61abeae339b3aa0ec7025ab238d89 --- .../AdvancedBluetoothDetailsHeaderControllerTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java index 49777bc7644..d24a83b9c4a 100644 --- a/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java +++ b/tests/robotests/src/com/android/settings/bluetooth/AdvancedBluetoothDetailsHeaderControllerTest.java @@ -59,7 +59,6 @@ import org.robolectric.annotation.Config; import java.util.HashSet; import java.util.Set; -@Ignore("b/313014781") @RunWith(RobolectricTestRunner.class) @Config(shadows = {ShadowEntityHeaderController.class, ShadowDeviceConfig.class}) public class AdvancedBluetoothDetailsHeaderControllerTest { @@ -386,6 +385,9 @@ public class AdvancedBluetoothDetailsHeaderControllerTest { .thenReturn("true".getBytes()); Set cacheBluetoothDevices = new HashSet<>(); when(mCachedDevice.getMemberDevice()).thenReturn(cacheBluetoothDevices); + when(mBluetoothAdapter.addOnMetadataChangedListener( + mBluetoothDevice, mContext.getMainExecutor(), mController.mMetadataListener)) + .thenReturn(true); mController.onStart(); From ce18c81da698354e84ae2d9c6f5cb664cba1e8d9 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 20 Dec 2023 17:03:59 +0800 Subject: [PATCH 2/8] Show "MMS messages" when mobile data is off For some corner case, - Single SIM, data off, auto switch on (although hidden) - Dual SIM, default data off, auto switch on Bug: 303759893 Test: manual - on Mobile Settings Test: unit test Change-Id: I70dd792ecff590f34100efaaf6ee4efcc2b759fd --- .../MmsMessagePreferenceController.kt | 23 +++- .../MmsMessagePreferenceControllerTest.kt | 101 +++++++++++++----- 2 files changed, 94 insertions(+), 30 deletions(-) diff --git a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt index 4e41038f068..bf954753922 100644 --- a/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt +++ b/src/com/android/settings/network/telephony/MmsMessagePreferenceController.kt @@ -29,8 +29,13 @@ import kotlinx.coroutines.flow.combine /** * Preference controller for "MMS messages" */ -class MmsMessagePreferenceController(context: Context, key: String) : - TelephonyTogglePreferenceController(context, key) { +class MmsMessagePreferenceController @JvmOverloads constructor( + context: Context, + key: String, + private val getDefaultDataSubId: () -> Int = { + SubscriptionManager.getDefaultDataSubscriptionId() + }, +) : TelephonyTogglePreferenceController(context, key) { private lateinit var telephonyManager: TelephonyManager @@ -46,10 +51,17 @@ class MmsMessagePreferenceController(context: Context, key: String) : if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID && !telephonyManager.isDataEnabled && telephonyManager.isApnMetered(ApnSetting.TYPE_MMS) && - !telephonyManager.isMobileDataPolicyEnabled( + !isFallbackDataEnabled() + ) AVAILABLE else CONDITIONALLY_UNAVAILABLE + + private fun isFallbackDataEnabled(): Boolean { + val defaultDataSubId = getDefaultDataSubId() + return defaultDataSubId != mSubId && + telephonyManager.createForSubscriptionId(defaultDataSubId).isDataEnabled && + telephonyManager.isMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH ) - ) AVAILABLE else CONDITIONALLY_UNAVAILABLE + } override fun displayPreference(screen: PreferenceScreen) { super.displayPreference(screen) @@ -61,7 +73,8 @@ class MmsMessagePreferenceController(context: Context, key: String) : mContext.mobileDataEnabledFlow(mSubId), mContext.subscriptionsChangedFlow(), // Capture isMobileDataPolicyEnabled() changes ) { _, _ -> }.collectLatestWithLifecycle(viewLifecycleOwner) { - preferenceScreen?.let { super.displayPreference(it) } } + preferenceScreen?.let { super.displayPreference(it) } + } } override fun isChecked(): Boolean = telephonyManager.isMobileDataPolicyEnabled( diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt index 588881941b9..a2f635d0b2a 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MmsMessagePreferenceControllerTest.kt @@ -27,7 +27,6 @@ import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILA import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.spy @@ -36,20 +35,35 @@ import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class MmsMessagePreferenceControllerTest { + private val mockTelephonyManager1: TelephonyManager = mock { + on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true + } + + private val mockTelephonyManager2: TelephonyManager = mock { + on { createForSubscriptionId(SUB_1_ID) } doReturn mockTelephonyManager1 + on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true + } + private val mockTelephonyManager: TelephonyManager = mock { - on { createForSubscriptionId(any()) } doReturn mock + on { createForSubscriptionId(SUB_1_ID) } doReturn mockTelephonyManager1 + on { createForSubscriptionId(SUB_2_ID) } doReturn mockTelephonyManager2 + on { createForSubscriptionId(INVALID_SUBSCRIPTION_ID) } doReturn mock } private var context: Context = spy(ApplicationProvider.getApplicationContext()) { on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager } - private val controller = MmsMessagePreferenceController(context, KEY).apply { - init(SUB_ID) - } + private var defaultDataSubId = SUB_1_ID + + private val controller = MmsMessagePreferenceController( + context = context, + key = KEY, + getDefaultDataSubId = { defaultDataSubId }, + ).apply { init(SUB_2_ID) } @Test - fun getAvailabilityStatus_invalidSubscription_returnUnavailable() { + fun getAvailabilityStatus_invalidSubscription_unavailable() { controller.init(INVALID_SUBSCRIPTION_ID) val availabilityStatus = controller.getAvailabilityStatus(INVALID_SUBSCRIPTION_ID) @@ -58,56 +72,92 @@ class MmsMessagePreferenceControllerTest { } @Test - fun getAvailabilityStatus_mobileDataOn_returnUnavailable() { - mockTelephonyManager.stub { + fun getAvailabilityStatus_mobileDataOn_unavailable() { + mockTelephonyManager2.stub { on { isDataEnabled } doReturn true } - val availabilityStatus = controller.getAvailabilityStatus(SUB_ID) + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) } @Test - fun getAvailabilityStatus_meteredOff_returnUnavailable() { - mockTelephonyManager.stub { + fun getAvailabilityStatus_meteredOff_unavailable() { + mockTelephonyManager2.stub { on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn false } - val availabilityStatus = controller.getAvailabilityStatus(SUB_ID) + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) } @Test - fun getAvailabilityStatus_autoDataSwitch_returnUnavailable() { - mockTelephonyManager.stub { - on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true + fun getAvailabilityStatus_isDefaultDataAndDataOnAndAutoDataSwitchOn_unavailable() { + defaultDataSubId = SUB_2_ID + mockTelephonyManager2.stub { + on { isDataEnabled } doReturn true on { isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) } doReturn true } - val availabilityStatus = controller.getAvailabilityStatus(SUB_ID) + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) } @Test - fun getAvailabilityStatus_mobileDataOffWithValidSubId_returnAvailable() { - mockTelephonyManager.stub { + fun getAvailabilityStatus_isDefaultDataAndDataOffAndAutoDataSwitchOn_available() { + defaultDataSubId = SUB_2_ID + mockTelephonyManager2.stub { on { isDataEnabled } doReturn false - on { isApnMetered(ApnSetting.TYPE_MMS) } doReturn true + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true } - val availabilityStatus = controller.getAvailabilityStatus(SUB_ID) + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) + + assertThat(availabilityStatus).isEqualTo(AVAILABLE) + } + + @Test + fun getAvailabilityStatus_defaultDataOnAndAutoDataSwitchOn_unavailable() { + mockTelephonyManager1.stub { + on { isDataEnabled } doReturn true + } + mockTelephonyManager2.stub { + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true + } + + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) + + assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) + } + + @Test + fun getAvailabilityStatus_defaultDataOffAndAutoDataSwitchOn_available() { + mockTelephonyManager1.stub { + on { isDataEnabled } doReturn false + } + mockTelephonyManager2.stub { + on { + isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH) + } doReturn true + } + + val availabilityStatus = controller.getAvailabilityStatus(SUB_2_ID) assertThat(availabilityStatus).isEqualTo(AVAILABLE) } @Test fun isChecked_whenMmsNotAlwaysAllowed_returnFalse() { - mockTelephonyManager.stub { + mockTelephonyManager2.stub { on { isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED) } doReturn false @@ -120,7 +170,7 @@ class MmsMessagePreferenceControllerTest { @Test fun isChecked_whenMmsAlwaysAllowed_returnTrue() { - mockTelephonyManager.stub { + mockTelephonyManager2.stub { on { isMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED) } doReturn true @@ -135,7 +185,7 @@ class MmsMessagePreferenceControllerTest { fun setChecked_setTrue_setDataIntoSubscriptionManager() { controller.setChecked(true) - verify(mockTelephonyManager).setMobileDataPolicyEnabled( + verify(mockTelephonyManager2).setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, true ) } @@ -144,13 +194,14 @@ class MmsMessagePreferenceControllerTest { fun setChecked_setFalse_setDataIntoSubscriptionManager() { controller.setChecked(false) - verify(mockTelephonyManager).setMobileDataPolicyEnabled( + verify(mockTelephonyManager2).setMobileDataPolicyEnabled( TelephonyManager.MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED, false ) } private companion object { const val KEY = "mms_message" - const val SUB_ID = 2 + const val SUB_1_ID = 1 + const val SUB_2_ID = 2 } } From 19dcf2dc1951eb0c663d5e79142366123fb774da Mon Sep 17 00:00:00 2001 From: Rhed Jao Date: Wed, 20 Dec 2023 09:47:51 +0000 Subject: [PATCH 3/8] Do not finish the activity if the activity result is required Repair mode requires the completion result after an user chooses a new screen lock. This change defers finishing the activity until the activity result is available. Bug: 281641188 Test: atest SettingsRoboTests:com.android.settings.password Change-Id: If635521ef7e1c509950d9683c15dffe45375cf4f --- .../android/settings/password/ChooseLockGeneric.java | 8 +++++--- .../settings/password/ChooseLockGenericTest.java | 10 ++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/password/ChooseLockGeneric.java b/src/com/android/settings/password/ChooseLockGeneric.java index 76e342e095c..663b4e5d090 100644 --- a/src/com/android/settings/password/ChooseLockGeneric.java +++ b/src/com/android/settings/password/ChooseLockGeneric.java @@ -162,6 +162,7 @@ public class ChooseLockGeneric extends SettingsActivity { private boolean mRequestGatekeeperPasswordHandle = false; private boolean mPasswordConfirmed = false; private boolean mWaitingForConfirmation = false; + private boolean mWaitingForActivityResult = false; private LockscreenCredential mUserPassword; private FingerprintManager mFingerprintManager; private FaceManager mFaceManager; @@ -470,6 +471,7 @@ public class ChooseLockGeneric extends SettingsActivity { public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); mWaitingForConfirmation = false; + mWaitingForActivityResult = false; if (requestCode == CONFIRM_EXISTING_REQUEST && resultCode == Activity.RESULT_OK) { mPasswordConfirmed = true; mUserPassword = data != null @@ -479,7 +481,6 @@ public class ChooseLockGeneric extends SettingsActivity { } else if (requestCode == CHOOSE_LOCK_REQUEST) { if (resultCode != RESULT_CANCELED) { getActivity().setResult(resultCode, data); - finish(); } else { // If PASSWORD_TYPE_KEY is set, this activity is used as a trampoline to start // the actual password enrollment. If the result is canceled, which means the @@ -487,9 +488,9 @@ public class ChooseLockGeneric extends SettingsActivity { int quality = getIntent().getIntExtra(LockPatternUtils.PASSWORD_TYPE_KEY, -1); if (quality != -1) { getActivity().setResult(RESULT_CANCELED, data); - finish(); } } + finish(); } else if (requestCode == CHOOSE_LOCK_BEFORE_BIOMETRIC_REQUEST && resultCode == BiometricEnrollBase.RESULT_FINISHED) { Intent intent = getBiometricEnrollIntent(getActivity()); @@ -821,6 +822,7 @@ public class ChooseLockGeneric extends SettingsActivity { } if (getIntent().getBooleanExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, false)) { intent.putExtra(EXTRA_KEY_REQUEST_WRITE_REPAIR_MODE_PW, true); + mWaitingForActivityResult = true; } intent.putExtra(EXTRA_CHOOSE_LOCK_GENERIC_EXTRAS, getIntent().getExtras()); // If the caller requested Gatekeeper Password Handle to be returned, we assume it @@ -871,7 +873,7 @@ public class ChooseLockGeneric extends SettingsActivity { // Otherwise, bugs would be caused. (e.g. b/278488549, b/278530059) final boolean hasCredential = mLockPatternUtils.isSecure(mUserId); if (!getActivity().isChangingConfigurations() - && !mWaitingForConfirmation && hasCredential) { + && !mWaitingForConfirmation && !mWaitingForActivityResult && hasCredential) { getActivity().finish(); } } diff --git a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java index ca1ab542c11..e7e0b921ad7 100644 --- a/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java +++ b/tests/robotests/src/com/android/settings/password/ChooseLockGenericTest.java @@ -304,6 +304,16 @@ public class ChooseLockGenericTest { assertThat(mActivity.isFinishing()).isTrue(); } + @Test + public void onActivityResult_requestcode102_resultCancel_shouldFinish() { + initActivity(null); + + mFragment.onActivityResult(ChooseLockGenericFragment.CHOOSE_LOCK_REQUEST, + Activity.RESULT_CANCELED, null /* data */); + + assertThat(mActivity.isFinishing()).isTrue(); + } + @Test public void onActivityResult_requestcode103_shouldFinish() { initActivity(null); From 6c0f9e68d1ab9a4088650474e9cc4434cb8693c7 Mon Sep 17 00:00:00 2001 From: Yanting Yang Date: Wed, 20 Dec 2023 13:29:21 +0000 Subject: [PATCH 4/8] Support search index for Accessibility button & gesture Bug: 223514494 Test: manual & atest AccessibilityButtonPreferenceControllerTest Change-Id: I2433e5aaccd8eb92325141246d9b409bb2b97e08 --- ...cessibilityButtonPreferenceController.java | 23 ++++++++++-- ...ibilityButtonPreferenceControllerTest.java | 36 +++++++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java b/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java index f0764721dcd..53d9c55fad9 100644 --- a/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java +++ b/src/com/android/settings/accessibility/AccessibilityButtonPreferenceController.java @@ -17,12 +17,16 @@ package com.android.settings.accessibility; import android.content.Context; +import android.content.res.Resources; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settings.R; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.search.SearchIndexableRaw; + +import java.util.List; /** * Preference controller for accessibility button preference. @@ -41,10 +45,23 @@ public class AccessibilityButtonPreferenceController extends BasePreferenceContr @Override public void displayPreference(PreferenceScreen screen) { super.displayPreference(screen); - final int titleResource = AccessibilityUtil.isGestureNavigateEnabled(mContext) - ? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title; final Preference preference = screen.findPreference(getPreferenceKey()); - preference.setTitle(titleResource); + preference.setTitle(getPreferenceTitleResource()); } + + @Override + public void updateDynamicRawDataToIndex(List rawData) { + SearchIndexableRaw data = new SearchIndexableRaw(mContext); + data.key = getPreferenceKey(); + final Resources res = mContext.getResources(); + data.title = res.getString(getPreferenceTitleResource()); + data.screenTitle = res.getString(R.string.accessibility_shortcuts_settings_title); + rawData.add(data); + } + + private int getPreferenceTitleResource() { + return AccessibilityUtil.isGestureNavigateEnabled(mContext) + ? R.string.accessibility_button_gesture_title : R.string.accessibility_button_title; + } } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java index 03f7887b9d0..ded9bcc96e3 100644 --- a/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityButtonPreferenceControllerTest.java @@ -31,6 +31,7 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settingslib.search.SearchIndexableRaw; import org.junit.Before; import org.junit.Rule; @@ -42,6 +43,9 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; +import java.util.ArrayList; +import java.util.List; + /** Tests for {@link AccessibilityButtonPreferenceController}. */ @RunWith(RobolectricTestRunner.class) public class AccessibilityButtonPreferenceControllerTest { @@ -88,4 +92,36 @@ public class AccessibilityButtonPreferenceControllerTest { assertThat(mPreference.getTitle()).isEqualTo( mContext.getText(R.string.accessibility_button_title)); } + + @Test + public void updateDynamicRawDataToIndex_navigationGestureEnabled_setCorrectIndex() { + when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)) + .thenReturn(NAV_BAR_MODE_GESTURAL); + List rawDataList = new ArrayList<>(); + + mController.updateDynamicRawDataToIndex(rawDataList); + + assertThat(rawDataList).hasSize(1); + SearchIndexableRaw raw = rawDataList.get(0); + assertThat(raw.title).isEqualTo( + mResources.getString(R.string.accessibility_button_gesture_title)); + assertThat(raw.screenTitle).isEqualTo( + mResources.getString(R.string.accessibility_shortcuts_settings_title)); + } + + @Test + public void updateDynamicRawDataToIndex_navigationGestureDisabled_setCorrectIndex() { + when(mResources.getInteger(com.android.internal.R.integer.config_navBarInteractionMode)) + .thenReturn(NAV_BAR_MODE_2BUTTON); + List rawDataList = new ArrayList<>(); + + mController.updateDynamicRawDataToIndex(rawDataList); + + assertThat(rawDataList).hasSize(1); + SearchIndexableRaw raw = rawDataList.get(0); + assertThat(raw.title).isEqualTo( + mResources.getString(R.string.accessibility_button_title)); + assertThat(raw.screenTitle).isEqualTo( + mResources.getString(R.string.accessibility_shortcuts_settings_title)); + } } From 5d58d4afaff705043f5cf4a38aae5b81867348e0 Mon Sep 17 00:00:00 2001 From: Charlotte Lu Date: Fri, 22 Dec 2023 13:02:54 +0800 Subject: [PATCH 5/8] Add WEP network strings. Test: Visual Test Fix: 317310222 Change-Id: I7ac6db481ba19af20d2004e2d333e391f0648738 --- res/values/strings.xml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/res/values/strings.xml b/res/values/strings.xml index f53af71d0fc..3eb67ab33e7 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1938,6 +1938,12 @@ Notify for public networks Notify when a high\u2011quality public network is available + + Allow WEP networks + + WEP is an older security protocol that\u0027s less secure + + Your carrier doesn\u0027t allow WEP networks because they\u0027re less secure Turn on Wi\u2011Fi automatically @@ -1987,6 +1993,18 @@ Turn on Wi\u2011Fi scanning turned on + + This network uses an older security protocol that\u0027s less secure + + %1$s is blocked + + This network uses an older security protocol called WEP, which is less secure. To connect anyway, you can allow WEP networks. + + Your carrier doesn\u0027t allow you to connect to this network because it uses an older security protocol that’s less secure + + Allow WEP + + Close From 55c8c9b86c116c5f172e264f5e58ace6edb7df24 Mon Sep 17 00:00:00 2001 From: Kevin Jeon Date: Thu, 14 Dec 2023 18:11:56 -0500 Subject: [PATCH 6/8] Hide Memory usage UI fields if flag is enabled This change updates the Memory use UI in developer settings to hide memory fields by default if the AppProfiler PSS deprecation flag is enabled. A toggle is provided to resume PSS profiling and reinstate the memory fields, and shows a dialog to reboot if the toggle is updated. The summary for the "Memory" button in developer settings will show "Memory profiling disabled" instead of "Avg X GB of Y memory used" if PSS profiling is not enabled. Test: With the flag enabled, flash and verify that: - The memory fields are hidden by default, and the summary shows the expected "Memory profiling disabled" text - Clicking the toggle shows the reboot dialog, which will correctly change the setting and reboot if accepted, and keep the setting and not reboot if denied - Check that AppProfiler correctly profiles PSS or RSS based on the state of the toggle, which persists across reboots - Included UI test for Memory page elements based on flag value. Bug: 296454553 Change-Id: Ie21791502445a321446cb8110e5800089f47ac58 --- res/values/strings.xml | 7 ++ res/xml/process_stats_summary.xml | 69 +++++++++-------- .../applications/ProcessStatsSummary.java | 68 ++++++++++++++++- .../DisableDevSettingsDialogFragment.java | 35 ++++++--- .../MemoryUsagePreferenceController.java | 12 ++- .../android/settings/ui/MemorySettingsTest.kt | 76 +++++++++++++++++++ 6 files changed, 222 insertions(+), 45 deletions(-) create mode 100644 tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt diff --git a/res/values/strings.xml b/res/values/strings.xml index f53af71d0fc..dc17c9ce9df 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9464,6 +9464,13 @@ other {# apps used memory in the last {time}} } + + Enable memory usage profiling + + Memory usage profiling requires additional system resources. + + Memory profiling disabled + Frequency diff --git a/res/xml/process_stats_summary.xml b/res/xml/process_stats_summary.xml index 3b3271d769a..9def18fa7b8 100644 --- a/res/xml/process_stats_summary.xml +++ b/res/xml/process_stats_summary.xml @@ -19,45 +19,52 @@ android:title="@string/app_memory_use" android:key="app_list"> + + + android:title="@string/average_memory_use" + android:key="memory_info"> - + - + - + - + - + - + - + - + + diff --git a/src/com/android/settings/applications/ProcessStatsSummary.java b/src/com/android/settings/applications/ProcessStatsSummary.java index 4044794e719..ef76cd5840b 100644 --- a/src/com/android/settings/applications/ProcessStatsSummary.java +++ b/src/com/android/settings/applications/ProcessStatsSummary.java @@ -16,20 +16,26 @@ package com.android.settings.applications; import android.app.settings.SettingsEnums; +import android.content.ContentResolver; import android.content.Context; import android.icu.text.MessageFormat; import android.os.Bundle; +import android.os.Flags; +import android.provider.Settings; import android.text.format.Formatter; import android.text.format.Formatter.BytesResult; import androidx.preference.Preference; import androidx.preference.Preference.OnPreferenceClickListener; +import androidx.preference.PreferenceCategory; +import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.SummaryPreference; import com.android.settings.Utils; import com.android.settings.applications.ProcStatsData.MemInfo; import com.android.settings.core.SubSettingLauncher; +import com.android.settings.development.DisableDevSettingsDialogFragment; import java.util.HashMap; import java.util.Locale; @@ -37,6 +43,8 @@ import java.util.Map; public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenceClickListener { + private static final String KEY_PREF_SCREEN = "app_list"; + private static final String KEY_MEMORY_INFO_PREF_GROUP = "memory_info"; private static final String KEY_STATUS_HEADER = "status_header"; private static final String KEY_PERFORMANCE = "performance"; @@ -44,7 +52,9 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc private static final String KEY_AVERAGY_USED = "average_used"; private static final String KEY_FREE = "free"; private static final String KEY_APP_LIST = "apps_list"; + private static final String KEY_FORCE_ENABLE_PSS_PROFILING = "force_enable_pss_profiling"; + private PreferenceCategory mMemoryInfoPrefCategory; private SummaryPreference mSummaryPref; private Preference mPerformance; @@ -52,12 +62,14 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc private Preference mAverageUsed; private Preference mFree; private Preference mAppListPreference; + private SwitchPreference mForceEnablePssProfiling; @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.process_stats_summary); + mMemoryInfoPrefCategory = (PreferenceCategory) findPreference(KEY_MEMORY_INFO_PREF_GROUP); mSummaryPref = (SummaryPreference) findPreference(KEY_STATUS_HEADER); mPerformance = findPreference(KEY_PERFORMANCE); mTotalMemory = findPreference(KEY_TOTAL_MEMORY); @@ -65,11 +77,37 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc mFree = findPreference(KEY_FREE); mAppListPreference = findPreference(KEY_APP_LIST); mAppListPreference.setOnPreferenceClickListener(this); + + // This preference is only applicable if the flag for PSS deprecation in AppProfiler is + // enabled. Otherwise, it can immediately be hidden. + mForceEnablePssProfiling = + (SwitchPreference) findPreference(KEY_FORCE_ENABLE_PSS_PROFILING); + if (Flags.removeAppProfilerPssCollection()) { + mForceEnablePssProfiling.setOnPreferenceClickListener(this); + // Make the toggle reflect the current state of the global setting. + mForceEnablePssProfiling.setChecked(isPssProfilingForceEnabled(getContext())); + } else { + mForceEnablePssProfiling.setVisible(false); + } + } + + private void refreshPreferences() { + // The memory fields should be static if the flag is not enabled. + if (!Flags.removeAppProfilerPssCollection()) { + return; + } + mMemoryInfoPrefCategory.setVisible(mForceEnablePssProfiling.isChecked()); } @Override public void refreshUi() { Context context = getContext(); + refreshPreferences(); + + // If PSS collection is not enabled, none of the following work needs to be done. + if (Flags.removeAppProfilerPssCollection() && !isPssProfilingForceEnabled(context)) { + return; + } MemInfo memInfo = mStatsManager.getMemInfo(); @@ -100,7 +138,8 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc String durationString = getString(sDurationLabels[mDurationIndex]); int numApps = mStatsManager.getEntries().size(); MessageFormat msgFormat = new MessageFormat( - getResources().getString(R.string.memory_usage_apps_summary), Locale.getDefault()); + getResources().getString(R.string.memory_usage_apps_summary), + Locale.getDefault()); Map arguments = new HashMap<>(); arguments.put("count", numApps); arguments.put("time", durationString); @@ -131,7 +170,34 @@ public class ProcessStatsSummary extends ProcessStatsBase implements OnPreferenc .setSourceMetricsCategory(getMetricsCategory()) .launch(); return true; + } else if (preference == mForceEnablePssProfiling) { + DisableDevSettingsDialogFragment.show(this); } return false; } + + private boolean isPssProfilingForceEnabled(Context context) { + ContentResolver cr = context.getContentResolver(); + return Settings.Global.getInt(cr, Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1; + } + + /** + * Called when the reboot confirmation button is clicked. + */ + public void onRebootDialogConfirmed() { + Context context = getContext(); + ContentResolver cr = context.getContentResolver(); + Settings.Global.putInt(cr, Settings.Global.FORCE_ENABLE_PSS_PROFILING, + mForceEnablePssProfiling.isChecked() ? 1 : 0); + refreshPreferences(); + } + + /** + * Called when the reboot deny button is clicked. + */ + public void onRebootDialogCanceled() { + // Set the toggle to reflect the state of the setting, which should not have changed. + mForceEnablePssProfiling.setChecked(isPssProfilingForceEnabled(getContext())); + } + } diff --git a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java index 224768b0561..4355f277cb3 100644 --- a/src/com/android/settings/development/DisableDevSettingsDialogFragment.java +++ b/src/com/android/settings/development/DisableDevSettingsDialogFragment.java @@ -29,6 +29,8 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import com.android.settings.R; +import com.android.settings.SettingsPreferenceFragment; +import com.android.settings.applications.ProcessStatsSummary; import com.android.settings.core.instrumentation.InstrumentedDialogFragment; public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment @@ -42,7 +44,7 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment return dialog; } - public static void show(DevelopmentSettingsDashboardFragment host) { + public static void show(SettingsPreferenceFragment host) { final DisableDevSettingsDialogFragment dialog = new DisableDevSettingsDialogFragment(); dialog.setTargetFragment(host, 0 /* requestCode */); // We need to handle data changes and switch state based on which button user clicks, @@ -75,18 +77,31 @@ public class DisableDevSettingsDialogFragment extends InstrumentedDialogFragment @Override public void onClick(DialogInterface dialog, int which) { Fragment fragment = getTargetFragment(); - if (!(fragment instanceof DevelopmentSettingsDashboardFragment)){ + if (!(fragment instanceof DevelopmentSettingsDashboardFragment) + && !(fragment instanceof ProcessStatsSummary)) { Log.e(TAG, "getTargetFragment return unexpected type"); } - final DevelopmentSettingsDashboardFragment host = - (DevelopmentSettingsDashboardFragment) fragment; - if (which == DialogInterface.BUTTON_POSITIVE) { - host.onDisableDevelopmentOptionsConfirmed(); - PowerManager pm = getContext().getSystemService(PowerManager.class); - pm.reboot(null); - } else { - host.onDisableDevelopmentOptionsRejected(); + if (fragment instanceof DevelopmentSettingsDashboardFragment) { + final DevelopmentSettingsDashboardFragment host = + (DevelopmentSettingsDashboardFragment) fragment; + if (which == DialogInterface.BUTTON_POSITIVE) { + host.onDisableDevelopmentOptionsConfirmed(); + PowerManager pm = getContext().getSystemService(PowerManager.class); + pm.reboot(null); + } else { + host.onDisableDevelopmentOptionsRejected(); + } + } else if (fragment instanceof ProcessStatsSummary) { + final ProcessStatsSummary host = + (ProcessStatsSummary) fragment; + if (which == DialogInterface.BUTTON_POSITIVE) { + host.onRebootDialogConfirmed(); + PowerManager pm = getContext().getSystemService(PowerManager.class); + pm.reboot(null); + } else { + host.onRebootDialogCanceled(); + } } } } diff --git a/src/com/android/settings/development/MemoryUsagePreferenceController.java b/src/com/android/settings/development/MemoryUsagePreferenceController.java index 1b20e70a578..ca8effa4a32 100644 --- a/src/com/android/settings/development/MemoryUsagePreferenceController.java +++ b/src/com/android/settings/development/MemoryUsagePreferenceController.java @@ -17,6 +17,8 @@ package com.android.settings.development; import android.content.Context; +import android.os.Flags; +import android.provider.Settings; import android.text.format.Formatter; import androidx.annotation.VisibleForTesting; @@ -65,9 +67,13 @@ public class MemoryUsagePreferenceController extends DeveloperOptionsPreferenceC (long) memInfo.realUsedRam); final String totalResult = Formatter.formatShortFileSize(mContext, (long) memInfo.realTotalRam); - ThreadUtils.postOnMainThread( - () -> mPreference.setSummary(mContext.getString(R.string.memory_summary, - usedResult, totalResult))); + boolean displayMemorySummary = !Flags.removeAppProfilerPssCollection(); + displayMemorySummary |= Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.FORCE_ENABLE_PSS_PROFILING, 0) == 1; + String summary = displayMemorySummary + ? mContext.getString(R.string.memory_summary, usedResult, totalResult) + : mContext.getString(R.string.pss_profiling_disabled); + ThreadUtils.postOnMainThread(() -> mPreference.setSummary(summary)); }); } diff --git a/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt b/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt new file mode 100644 index 00000000000..312e2c62bf2 --- /dev/null +++ b/tests/uitests/src/com/android/settings/ui/MemorySettingsTest.kt @@ -0,0 +1,76 @@ +/* + * 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.ui + +import android.os.Flags +import android.platform.test.annotations.RequiresFlagsDisabled +import android.platform.test.annotations.RequiresFlagsEnabled +import android.provider.Settings +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import com.android.settings.ui.testutils.SettingsTestUtils.assertHasTexts +import com.android.settings.ui.testutils.SettingsTestUtils.clickObject +import com.android.settings.ui.testutils.SettingsTestUtils.startMainActivityFromHomeScreen +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +@SmallTest +class MemorySettingsTest { + private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) + + @Before + fun setUp() { + device.startMainActivityFromHomeScreen(Settings.ACTION_DEVICE_INFO_SETTINGS) + device.assertHasTexts(listOf(BUILD_NUMBER)) + repeat(7) { // Enable development mode + device.clickObject(By.text(BUILD_NUMBER)) + } + device.startMainActivityFromHomeScreen(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS) + device.clickObject(By.text(MEMORY_PAGE)) + } + + @Test + @RequiresFlagsDisabled(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION) + fun memoryPageIfPssFlagDisabled() { + device.assertHasTexts(ON_SCREEN_TEXTS_DEFAULT) + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_REMOVE_APP_PROFILER_PSS_COLLECTION) + fun memoryPageIfPssFlagEnabled() { + device.assertHasTexts(ON_SCREEN_TEXTS_PSS_PROFILING_DISABLED) + } + + private companion object { + private const val BUILD_NUMBER = "Build number" + private const val MEMORY_PAGE = "Memory" + val ON_SCREEN_TEXTS_DEFAULT = listOf( + "Performance", + "Total memory", + "Average used (%)", + "Free", + ) + val ON_SCREEN_TEXTS_PSS_PROFILING_DISABLED = listOf( + "Enable memory usage profiling", + ) + } +} From 480d523a0c19d41a886eafb38913d61113f74bb3 Mon Sep 17 00:00:00 2001 From: Graciela Wissen Putri Date: Fri, 15 Dec 2023 16:15:03 +0000 Subject: [PATCH 7/8] [3/n] Pre-select fullscreen option if overridden Settings > Apps > Aspect ratio (experimental) Pre-select the fullscreen option if all are true: - device manufacturer has overridden app to fullscreen - app has not opted out of orientation override - app has not opted out of user's fullscreen option - user has not set any aspect ratio (USER_MIN_ASPECT_RATIO_UNSET) "App default" will have a new value of USER_MIN_ASPECT_RATIO_APP_DEFAULT if device manufacturer has overridden app to fullscreen, which will behave the same as USER_MIN_ASPECT_RATIO_UNSET without the device-applied fullscreen override. Bug: 310816437 Test: atest UserAspectRatioDetailsTest atest UserAspectRatioManagerTest atest UserAspectRatioAppPreferenceTest Change-Id: I6be634bb4369292687b865ce30d902540419183c --- .../appcompat/UserAspectRatioDetails.java | 75 ++++---- .../appcompat/UserAspectRatioManager.java | 132 ++++++++++--- .../UserAspectRatioAppsPageProvider.kt | 11 +- .../appcompat/UserAspectRatioDetailsTest.java | 71 ++++++- tests/spa_unit/AndroidManifest.xml | 2 + .../UserAspectRatioAppPreferenceTest.kt | 4 + .../appcompat/UserAspectRatioManagerTest.java | 173 +++++++++++++++--- 7 files changed, 372 insertions(+), 96 deletions(-) diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java index 02d5c27e5c6..ef054dfa405 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioDetails.java @@ -21,6 +21,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; @@ -51,6 +52,9 @@ import com.android.settingslib.applications.AppUtils; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.widget.ActionButtonsPreference; +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; + import java.util.ArrayList; import java.util.List; @@ -63,29 +67,41 @@ public class UserAspectRatioDetails extends AppInfoBase implements private static final String KEY_HEADER_SUMMARY = "app_aspect_ratio_summary"; private static final String KEY_HEADER_BUTTONS = "header_view"; - private static final String KEY_PREF_FULLSCREEN = "fullscreen_pref"; + private static final String KEY_PREF_HALF_SCREEN = "half_screen_pref"; private static final String KEY_PREF_DISPLAY_SIZE = "display_size_pref"; private static final String KEY_PREF_16_9 = "16_9_pref"; private static final String KEY_PREF_4_3 = "4_3_pref"; @VisibleForTesting + static final String KEY_PREF_FULLSCREEN = "fullscreen_pref"; + @VisibleForTesting static final String KEY_PREF_DEFAULT = "app_default_pref"; @VisibleForTesting static final String KEY_PREF_3_2 = "3_2_pref"; + @VisibleForTesting + @NonNull String mSelectedKey = KEY_PREF_DEFAULT; + + /** Radio button preference key mapped to {@link PackageManager.UserMinAspectRatio} value */ + @VisibleForTesting + final BiMap mKeyToAspectRatioMap = HashBiMap.create(); + private final List mAspectRatioPreferences = new ArrayList<>(); @NonNull private UserAspectRatioManager mUserAspectRatioManager; - @NonNull private String mSelectedKey = KEY_PREF_DEFAULT; + private boolean mIsOverrideToFullscreenEnabled; @Override public void onCreate(@NonNull Bundle savedInstanceState) { super.onCreate(savedInstanceState); mUserAspectRatioManager = new UserAspectRatioManager(getContext()); + mIsOverrideToFullscreenEnabled = getAspectRatioManager() + .isOverrideToFullscreenEnabled(mPackageName, mUserId); + initPreferences(); try { - final int userAspectRatio = mUserAspectRatioManager + final int userAspectRatio = getAspectRatioManager() .getUserMinAspectRatioValue(mPackageName, mUserId); mSelectedKey = getSelectedKey(userAspectRatio); } catch (RemoteException e) { @@ -148,43 +164,23 @@ public class UserAspectRatioDetails extends AppInfoBase implements } @PackageManager.UserMinAspectRatio - private int getSelectedUserMinAspectRatio(@NonNull String selectedKey) { - switch (selectedKey) { - case KEY_PREF_FULLSCREEN: - return USER_MIN_ASPECT_RATIO_FULLSCREEN; - case KEY_PREF_HALF_SCREEN: - return USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; - case KEY_PREF_DISPLAY_SIZE: - return USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; - case KEY_PREF_3_2: - return USER_MIN_ASPECT_RATIO_3_2; - case KEY_PREF_4_3: - return USER_MIN_ASPECT_RATIO_4_3; - case KEY_PREF_16_9: - return USER_MIN_ASPECT_RATIO_16_9; - default: - return USER_MIN_ASPECT_RATIO_UNSET; - } + @VisibleForTesting + int getSelectedUserMinAspectRatio(@NonNull String selectedKey) { + final int appDefault = mKeyToAspectRatioMap + .getOrDefault(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + return mKeyToAspectRatioMap.getOrDefault(selectedKey, appDefault); } @NonNull private String getSelectedKey(@PackageManager.UserMinAspectRatio int userMinAspectRatio) { - switch (userMinAspectRatio) { - case USER_MIN_ASPECT_RATIO_FULLSCREEN: - return KEY_PREF_FULLSCREEN; - case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: - return KEY_PREF_HALF_SCREEN; - case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: - return KEY_PREF_DISPLAY_SIZE; - case USER_MIN_ASPECT_RATIO_3_2: - return KEY_PREF_3_2; - case USER_MIN_ASPECT_RATIO_4_3: - return KEY_PREF_4_3; - case USER_MIN_ASPECT_RATIO_16_9: - return KEY_PREF_16_9; - default: - return KEY_PREF_DEFAULT; + final String appDefault = mKeyToAspectRatioMap.inverse() + .getOrDefault(USER_MIN_ASPECT_RATIO_UNSET, KEY_PREF_DEFAULT); + + if (userMinAspectRatio == USER_MIN_ASPECT_RATIO_UNSET && mIsOverrideToFullscreenEnabled) { + // Pre-select fullscreen option if device manufacturer has overridden app to fullscreen + userMinAspectRatio = USER_MIN_ASPECT_RATIO_FULLSCREEN; } + return mKeyToAspectRatioMap.inverse().getOrDefault(userMinAspectRatio, appDefault); } @Override @@ -217,7 +213,11 @@ public class UserAspectRatioDetails extends AppInfoBase implements .setButton1Icon(R.drawable.ic_settings_open) .setButton1OnClickListener(v -> launchApplication()); - addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + if (mIsOverrideToFullscreenEnabled) { + addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_APP_DEFAULT); + } else { + addPreference(KEY_PREF_DEFAULT, USER_MIN_ASPECT_RATIO_UNSET); + } addPreference(KEY_PREF_FULLSCREEN, USER_MIN_ASPECT_RATIO_FULLSCREEN); addPreference(KEY_PREF_DISPLAY_SIZE, USER_MIN_ASPECT_RATIO_DISPLAY_SIZE); addPreference(KEY_PREF_HALF_SCREEN, USER_MIN_ASPECT_RATIO_SPLIT_SCREEN); @@ -232,12 +232,13 @@ public class UserAspectRatioDetails extends AppInfoBase implements if (pref == null) { return; } - if (!mUserAspectRatioManager.hasAspectRatioOption(aspectRatio, mPackageName)) { + if (!getAspectRatioManager().hasAspectRatioOption(aspectRatio, mPackageName)) { pref.setVisible(false); return; } pref.setTitle(mUserAspectRatioManager.getAccessibleEntry(aspectRatio, mPackageName)); pref.setOnClickListener(this); + mKeyToAspectRatioMap.put(key, aspectRatio); mAspectRatioPreferences.add(pref); } diff --git a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java index 3cca5f6771e..3bf61099f13 100644 --- a/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java +++ b/src/com/android/settings/applications/appcompat/UserAspectRatioManager.java @@ -16,19 +16,32 @@ package com.android.settings.applications.appcompat; +import static android.content.pm.ActivityInfo.OVERRIDE_ANY_ORIENTATION_TO_USER; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_16_9; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_3_2; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_4_3; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; import static android.os.UserHandle.getUserHandleForUid; +import static android.os.UserHandle.getUserId; +import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE; import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE; import static java.lang.Boolean.FALSE; import android.app.AppGlobals; +import android.app.compat.CompatChanges; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.os.RemoteException; +import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.ArrayMap; @@ -37,6 +50,7 @@ import androidx.annotation.Nullable; import com.android.settings.R; import com.android.settings.Utils; +import com.android.window.flags.Flags; import com.google.common.annotations.VisibleForTesting; @@ -55,6 +69,8 @@ public class UserAspectRatioManager { "enable_app_compat_user_aspect_ratio_fullscreen"; private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN = true; + final boolean mIsUserMinAspectRatioAppDefaultFlagEnabled = Flags.userMinAspectRatioAppDefault(); + private final Context mContext; private final IPackageManager mIPm; /** Apps that have launcher entry defined in manifest */ @@ -62,8 +78,13 @@ public class UserAspectRatioManager { private final Map mUserAspectRatioA11yMap; public UserAspectRatioManager(@NonNull Context context) { + this(context, AppGlobals.getPackageManager()); + } + + @VisibleForTesting + UserAspectRatioManager(@NonNull Context context, @NonNull IPackageManager pm) { mContext = context; - mIPm = AppGlobals.getPackageManager(); + mIPm = pm; mUserAspectRatioA11yMap = new ArrayMap<>(); mUserAspectRatioMap = getUserMinAspectRatioMapping(); } @@ -86,7 +107,7 @@ public class UserAspectRatioManager { throws RemoteException { final int aspectRatio = mIPm.getUserMinAspectRatio(packageName, uid); return hasAspectRatioOption(aspectRatio, packageName) - ? aspectRatio : PackageManager.USER_MIN_ASPECT_RATIO_UNSET; + ? aspectRatio : USER_MIN_ASPECT_RATIO_UNSET; } /** @@ -94,11 +115,18 @@ public class UserAspectRatioManager { */ @NonNull public String getUserMinAspectRatioEntry(@PackageManager.UserMinAspectRatio int aspectRatio, - String packageName) { - if (!hasAspectRatioOption(aspectRatio, packageName)) { - return mUserAspectRatioMap.get(PackageManager.USER_MIN_ASPECT_RATIO_UNSET); + @NonNull String packageName, int userId) { + final String appDefault = getAspectRatioStringOrDefault( + mUserAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET), + USER_MIN_ASPECT_RATIO_UNSET); + + if (!hasAspectRatioOption(aspectRatio, packageName)) { + return appDefault; } - return mUserAspectRatioMap.get(aspectRatio); + + return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio) + ? getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName, userId) + : mUserAspectRatioMap.getOrDefault(aspectRatio, appDefault); } /** @@ -106,19 +134,22 @@ public class UserAspectRatioManager { */ @NonNull public CharSequence getAccessibleEntry(@PackageManager.UserMinAspectRatio int aspectRatio, - String packageName) { - return mUserAspectRatioA11yMap.getOrDefault(aspectRatio, - getUserMinAspectRatioEntry(aspectRatio, packageName)); + @NonNull String packageName) { + final int userId = mContext.getUserId(); + return isCurrentSelectionFromManufacturerOverride(packageName, userId, aspectRatio) + ? getAccessibleEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, packageName) + : mUserAspectRatioA11yMap.getOrDefault(aspectRatio, + getUserMinAspectRatioEntry(aspectRatio, packageName, userId)); } /** * @return corresponding aspect ratio string for package name and user */ @NonNull - public String getUserMinAspectRatioEntry(@NonNull String packageName, int uid) + public String getUserMinAspectRatioEntry(@NonNull String packageName, int userId) throws RemoteException { - final int aspectRatio = getUserMinAspectRatioValue(packageName, uid); - return getUserMinAspectRatioEntry(aspectRatio, packageName); + final int aspectRatio = getUserMinAspectRatioValue(packageName, userId); + return getUserMinAspectRatioEntry(aspectRatio, packageName, userId); } /** @@ -128,8 +159,7 @@ public class UserAspectRatioManager { */ public boolean hasAspectRatioOption(@PackageManager.UserMinAspectRatio int option, String packageName) { - if (option == PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN - && !isFullscreenOptionEnabled(packageName)) { + if (option == USER_MIN_ASPECT_RATIO_FULLSCREEN && !isFullscreenOptionEnabled(packageName)) { return false; } return mUserAspectRatioMap.containsKey(option); @@ -154,6 +184,18 @@ public class UserAspectRatioManager { return !FALSE.equals(appAllowsUserAspectRatioOverride) && hasLauncherEntry(app); } + /** + * Whether the app has been overridden to fullscreen by device manufacturer or + * whether the app's aspect ratio has been overridden by the user. + */ + public boolean isAppOverridden(@NonNull ApplicationInfo app, + @PackageManager.UserMinAspectRatio int userOverride) { + return (userOverride != USER_MIN_ASPECT_RATIO_UNSET + && userOverride != USER_MIN_ASPECT_RATIO_APP_DEFAULT) + || isCurrentSelectionFromManufacturerOverride(app.packageName, getUserId(app.uid), + userOverride); + } + /** * Whether fullscreen option in per-app user aspect ratio settings is enabled */ @@ -168,6 +210,32 @@ public class UserAspectRatioManager { DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); } + /** + * Whether the device manufacturer has overridden app's orientation to + * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_USER} to force app to fullscreen + * and app has not opted-out from the treatment + */ + boolean isOverrideToFullscreenEnabled(String pkgName, int userId) { + Boolean appAllowsOrientationOverride = readComponentProperty(mContext.getPackageManager(), + pkgName, PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE); + return mIsUserMinAspectRatioAppDefaultFlagEnabled + && hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, pkgName) + && !FALSE.equals(appAllowsOrientationOverride) + && isFullscreenCompatChangeEnabled(pkgName, userId); + } + + @VisibleForTesting + boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) { + return CompatChanges.isChangeEnabled( + OVERRIDE_ANY_ORIENTATION_TO_USER, pkgName, UserHandle.of(userId)); + } + + private boolean isCurrentSelectionFromManufacturerOverride(String pkgName, int userId, + @PackageManager.UserMinAspectRatio int aspectRatio) { + return aspectRatio == USER_MIN_ASPECT_RATIO_UNSET + && isOverrideToFullscreenEnabled(pkgName, userId); + } + private boolean hasLauncherEntry(@NonNull ApplicationInfo app) { return !mContext.getSystemService(LauncherApps.class) .getActivityList(app.packageName, getUserHandleForUid(app.uid)) @@ -197,13 +265,13 @@ public class UserAspectRatioManager { boolean containsColon = aspectRatioString.contains(":"); switch (aspectRatioVal) { // Only map known values of UserMinAspectRatio and ignore unknown entries - case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN: - case PackageManager.USER_MIN_ASPECT_RATIO_UNSET: - case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: - case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: - case PackageManager.USER_MIN_ASPECT_RATIO_4_3: - case PackageManager.USER_MIN_ASPECT_RATIO_16_9: - case PackageManager.USER_MIN_ASPECT_RATIO_3_2: + case USER_MIN_ASPECT_RATIO_FULLSCREEN: + case USER_MIN_ASPECT_RATIO_UNSET: + case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: + case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: + case USER_MIN_ASPECT_RATIO_4_3: + case USER_MIN_ASPECT_RATIO_16_9: + case USER_MIN_ASPECT_RATIO_3_2: if (containsColon) { String[] aspectRatioDigits = aspectRatioString.split(":"); String accessibleString = getAccessibleOption(aspectRatioDigits[0], @@ -215,10 +283,18 @@ public class UserAspectRatioManager { userMinAspectRatioMap.put(aspectRatioVal, aspectRatioString); } } - if (!userMinAspectRatioMap.containsKey(PackageManager.USER_MIN_ASPECT_RATIO_UNSET)) { + if (!userMinAspectRatioMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) { throw new RuntimeException("config_userAspectRatioOverrideValues options must have" + " USER_MIN_ASPECT_RATIO_UNSET value"); } + if (mIsUserMinAspectRatioAppDefaultFlagEnabled) { + userMinAspectRatioMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + userMinAspectRatioMap.get(USER_MIN_ASPECT_RATIO_UNSET)); + if (mUserAspectRatioA11yMap.containsKey(USER_MIN_ASPECT_RATIO_UNSET)) { + mUserAspectRatioA11yMap.put(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + mUserAspectRatioA11yMap.get(USER_MIN_ASPECT_RATIO_UNSET)); + } + } return userMinAspectRatioMap; } @@ -236,17 +312,17 @@ public class UserAspectRatioManager { } // Options are customized per device and if strings are set to @null, use default switch (aspectRatioVal) { - case PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN: + case USER_MIN_ASPECT_RATIO_FULLSCREEN: return mContext.getString(R.string.user_aspect_ratio_fullscreen); - case PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: + case USER_MIN_ASPECT_RATIO_SPLIT_SCREEN: return mContext.getString(R.string.user_aspect_ratio_half_screen); - case PackageManager.USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: + case USER_MIN_ASPECT_RATIO_DISPLAY_SIZE: return mContext.getString(R.string.user_aspect_ratio_device_size); - case PackageManager.USER_MIN_ASPECT_RATIO_4_3: + case USER_MIN_ASPECT_RATIO_4_3: return mContext.getString(R.string.user_aspect_ratio_4_3); - case PackageManager.USER_MIN_ASPECT_RATIO_16_9: + case USER_MIN_ASPECT_RATIO_16_9: return mContext.getString(R.string.user_aspect_ratio_16_9); - case PackageManager.USER_MIN_ASPECT_RATIO_3_2: + case USER_MIN_ASPECT_RATIO_3_2: return mContext.getString(R.string.user_aspect_ratio_3_2); default: return mContext.getString(R.string.user_aspect_ratio_app_default); diff --git a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt index 7368750fce5..15cd1bab5f6 100644 --- a/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt +++ b/src/com/android/settings/spa/app/appcompat/UserAspectRatioAppsPageProvider.kt @@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo import android.content.pm.PackageManager import android.content.pm.PackageManager.GET_ACTIVITIES +import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT import android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET import android.os.Build import android.os.Bundle @@ -139,7 +140,9 @@ class UserAspectRatioAppListModel(private val context: Context) recordList: List ): List { val hasSuggested = recordList.any { it.suggested } - val hasOverride = recordList.any { it.userOverride != USER_MIN_ASPECT_RATIO_UNSET } + val hasOverride = recordList.any { + userAspectRatioManager.isAppOverridden(it.app, it.userOverride) + } val options = mutableListOf(SpinnerItem.All) // Add suggested filter first as default if (hasSuggested) options.add(0, SpinnerItem.Suggested) @@ -187,7 +190,9 @@ class UserAspectRatioAppListModel(private val context: Context) ): Flow> = recordListFlow.filterItem( when (SpinnerItem.entries.getOrNull(option)) { SpinnerItem.Suggested -> ({ it.canDisplay && it.suggested }) - SpinnerItem.Overridden -> ({ it.userOverride != USER_MIN_ASPECT_RATIO_UNSET }) + SpinnerItem.Overridden -> ({ + userAspectRatioManager.isAppOverridden(it.app, it.userOverride) + }) else -> ({ it.canDisplay }) } ) @@ -197,7 +202,7 @@ class UserAspectRatioAppListModel(private val context: Context) val summary by remember(record.userOverride) { flow { emit(userAspectRatioManager.getUserMinAspectRatioEntry(record.userOverride, - record.app.packageName)) + record.app.packageName, record.app.userId)) }.flowOn(Dispatchers.IO) }.collectAsStateWithLifecycle(initialValue = stringResource(R.string.summary_placeholder)) return { summary } diff --git a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java index b615163fbc7..f3b105bc3d6 100644 --- a/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java +++ b/tests/robotests/src/com/android/settings/applications/appcompat/UserAspectRatioDetailsTest.java @@ -16,12 +16,21 @@ package com.android.settings.applications.appcompat; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_APP_DEFAULT; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_FULLSCREEN; +import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET; + +import static com.android.settings.applications.AppInfoBase.ARG_PACKAGE_NAME; import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_3_2; import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_DEFAULT; +import static com.android.settings.applications.appcompat.UserAspectRatioDetails.KEY_PREF_FULLSCREEN; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -30,15 +39,20 @@ import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.settings.SettingsEnums; import android.content.Context; +import android.os.Bundle; import android.os.RemoteException; +import androidx.fragment.app.testing.EmptyFragmentActivity; import androidx.test.core.app.ApplicationProvider; +import androidx.test.ext.junit.rules.ActivityScenarioRule; import com.android.settings.testutils.FakeFeatureFactory; import com.android.settings.testutils.shadow.ShadowActivityManager; +import com.android.settings.testutils.shadow.ShadowFragment; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; @@ -51,9 +65,13 @@ import org.robolectric.annotation.Config; * To run test: atest SettingsRoboTests:UserAspectRatioDetailsTest */ @RunWith(RobolectricTestRunner.class) -@Config(shadows = {ShadowActivityManager.class}) +@Config(shadows = {ShadowActivityManager.class, ShadowFragment.class}) public class UserAspectRatioDetailsTest { + @Rule + public ActivityScenarioRule rule = + new ActivityScenarioRule<>(EmptyFragmentActivity.class); + @Mock private UserAspectRatioManager mUserAspectRatioManager; @Mock @@ -72,6 +90,8 @@ public class UserAspectRatioDetailsTest { mFragment = spy(new UserAspectRatioDetails()); when(mFragment.getContext()).thenReturn(mContext); when(mFragment.getAspectRatioManager()).thenReturn(mUserAspectRatioManager); + when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt())) + .thenReturn(false); ShadowActivityManager.setService(mAm); mRadioButtonPref = new RadioWithImagePreference(mContext); final FakeFeatureFactory featureFactory = FakeFeatureFactory.setupForTest(); @@ -80,6 +100,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_shouldStopActivity() throws RemoteException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -92,6 +114,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_shouldSetAspectRatio() throws RemoteException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -105,6 +129,8 @@ public class UserAspectRatioDetailsTest { @Test public void onRadioButtonClicked_prefChange_logMetrics() throws NullPointerException { + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mFragment) + .getSelectedUserMinAspectRatio(anyString()); // Default was already selected mRadioButtonPref.setKey(KEY_PREF_DEFAULT); mFragment.onRadioButtonClicked(mRadioButtonPref); @@ -129,4 +155,47 @@ public class UserAspectRatioDetailsTest { any(), anyInt()); } + + @Test + public void onButtonClicked_overrideEnabled_fullscreenPreselected() + throws RemoteException { + doReturn(true).when(mUserAspectRatioManager) + .isOverrideToFullscreenEnabled(anyString(), anyInt()); + doReturn(USER_MIN_ASPECT_RATIO_UNSET).when(mUserAspectRatioManager) + .getUserMinAspectRatioValue(anyString(), anyInt()); + doReturn(mRadioButtonPref).when(mFragment).findPreference(KEY_PREF_DEFAULT); + doReturn(mRadioButtonPref).when(mFragment).findPreference(KEY_PREF_FULLSCREEN); + doReturn(true).when(mUserAspectRatioManager) + .hasAspectRatioOption(anyInt(), anyString()); + + rule.getScenario().onActivity(a -> doReturn(a).when(mFragment).getActivity()); + final Bundle args = new Bundle(); + args.putString(ARG_PACKAGE_NAME, anyString()); + mFragment.setArguments(args); + mFragment.onCreate(Bundle.EMPTY); + + // Fullscreen should be pre-selected + assertEquals(KEY_PREF_FULLSCREEN, mFragment.mSelectedKey); + assertEquals(USER_MIN_ASPECT_RATIO_FULLSCREEN, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + + // Revert to app default, should be set to app default from unset + mRadioButtonPref.setKey(KEY_PREF_DEFAULT); + mFragment.onRadioButtonClicked(mRadioButtonPref); + verify(mUserAspectRatioManager).setUserMinAspectRatio( + any(), anyInt(), anyInt()); + assertEquals(USER_MIN_ASPECT_RATIO_APP_DEFAULT, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + assertEquals(KEY_PREF_DEFAULT, mFragment.mSelectedKey); + + // Fullscreen override disabled, should be changed to unset from app default + when(mUserAspectRatioManager.isOverrideToFullscreenEnabled(anyString(), anyInt())) + .thenReturn(false); + mFragment.mKeyToAspectRatioMap.clear(); + mFragment.onCreate(Bundle.EMPTY); + + assertEquals(KEY_PREF_DEFAULT, mFragment.mSelectedKey); + assertEquals(USER_MIN_ASPECT_RATIO_UNSET, + mFragment.getSelectedUserMinAspectRatio(mFragment.mSelectedKey)); + } } diff --git a/tests/spa_unit/AndroidManifest.xml b/tests/spa_unit/AndroidManifest.xml index 5a7f5659ce6..51ac1b740e9 100644 --- a/tests/spa_unit/AndroidManifest.xml +++ b/tests/spa_unit/AndroidManifest.xml @@ -22,6 +22,8 @@ + + mLauncherActivities; @Before - public void setUp() { + public void setUp() throws RemoteException, PackageManager.NameNotFoundException { mContext = spy(ApplicationProvider.getApplicationContext()); mResources = mock(Resources.class); - mLauncherApps = mock(LauncherApps.class); + final LauncherApps launcherApps = mock(LauncherApps.class); mLauncherActivities = mock(List.class); + mIPm = mock(IPackageManager.class); + mPm = mock(PackageManager.class); + when(mContext.getPackageManager()).thenReturn(mPm); when(mContext.getResources()).thenReturn(mResources); - when(mContext.getSystemService(LauncherApps.class)).thenReturn(mLauncherApps); + when(mContext.getSystemService(LauncherApps.class)).thenReturn(launcherApps); enableAllDefaultAspectRatioOptions(); + mSetFlagsRule.disableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); - mUtils = new UserAspectRatioManager(mContext); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); - doReturn(mLauncherActivities).when(mLauncherApps).getActivityList(anyString(), any()); + doReturn(mLauncherActivities).when(launcherApps).getActivityList(anyString(), any()); mOriginalSettingsFlag = DeviceConfig.getProperty( DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_SETTINGS); @@ -102,6 +117,10 @@ public class UserAspectRatioManagerTest { DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN); setAspectRatioFullscreenBuildTimeFlagEnabled(true); setAspectRatioFullscreenDeviceConfigEnabled("true" /* enabled */, false /* makeDefault */); + + mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_FULLSCREEN_OVERRIDE, true); + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true); + mockProperty(PROPERTY_COMPAT_ALLOW_USER_ASPECT_RATIO_OVERRIDE, true); } @After @@ -114,13 +133,13 @@ public class UserAspectRatioManagerTest { @Test public void testCanDisplayAspectRatioUi() { final ApplicationInfo canDisplay = new ApplicationInfo(); - canDisplay.packageName = "com.app.candisplay"; + canDisplay.packageName = mPackageName; doReturn(false).when(mLauncherActivities).isEmpty(); assertTrue(mUtils.canDisplayAspectRatioUi(canDisplay)); final ApplicationInfo noLauncherEntry = new ApplicationInfo(); - noLauncherEntry.packageName = "com.app.nolauncherentry"; + noLauncherEntry.packageName = mPackageName; doReturn(true).when(mLauncherActivities).isEmpty(); assertFalse(mUtils.canDisplayAspectRatioUi(noLauncherEntry)); @@ -221,36 +240,40 @@ public class UserAspectRatioManagerTest { mPackageName)); } + private String getUserMinAspectRatioEntry(int aspectRatio, String packageName) { + return mUtils.getUserMinAspectRatioEntry(aspectRatio, packageName, mContext.getUserId()); + } + @Test public void testGetUserMinAspectRatioEntry() { final Context context = ApplicationProvider.getApplicationContext(); // R.string.user_aspect_ratio_app_default final String appDefault = ResourcesUtils.getResourcesString(context, "user_aspect_ratio_app_default"); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) .isEqualTo(appDefault); // should always return default if value does not correspond to anything - assertThat(mUtils.getUserMinAspectRatioEntry(-1, mPackageName)) + assertThat(getUserMinAspectRatioEntry(-1, mPackageName)) .isEqualTo(appDefault); // R.string.user_aspect_ratio_half_screen - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_SPLIT_SCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_half_screen")); // R.string.user_aspect_ratio_display_size - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_DISPLAY_SIZE, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_device_size")); // R.string.user_aspect_ratio_16_9 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_16_9, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_16_9")); // R.string.user_aspect_ratio_4_3 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_4_3, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_4_3")); // R.string.user_aspect_ratio_3_2 - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_3_2, mPackageName)) .isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_3_2")); // R.string.user_aspect_ratio_fullscreen - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString(context, "user_aspect_ratio_fullscreen")); } @@ -258,7 +281,7 @@ public class UserAspectRatioManagerTest { @Test public void testGetUserMinAspectRatioEntry_fullscreenDisabled_shouldReturnDefault() { setAspectRatioFullscreenBuildTimeFlagEnabled(false); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)).isEqualTo(ResourcesUtils.getResourcesString( ApplicationProvider.getApplicationContext(), "user_aspect_ratio_app_default")); @@ -270,9 +293,9 @@ public class UserAspectRatioManagerTest { when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_UNSET}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {newOptionName}); - mUtils = new UserAspectRatioManager(mContext); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); - assertThat(mUtils.getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) .isEqualTo(newOptionName); } @@ -282,7 +305,7 @@ public class UserAspectRatioManagerTest { when(mResources.getIntArray(anyInt())).thenReturn(new int[] {USER_MIN_ASPECT_RATIO_4_3}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"}); - assertThrows(RuntimeException.class, () -> new UserAspectRatioManager(mContext)); + assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm)); } @Test @@ -292,7 +315,88 @@ public class UserAspectRatioManagerTest { USER_MIN_ASPECT_RATIO_4_3}); when(mResources.getStringArray(anyInt())).thenReturn(new String[] {"4:3"}); - assertThrows(RuntimeException.class, () -> new UserAspectRatioManager(mContext)); + assertThrows(RuntimeException.class, () -> new FakeUserAspectRatioManager(mContext, mIPm)); + } + + @Test + public void testGetUserMinAspectRatioMapping_appDefaultFlagEnabled() { + // Flag is disabled by default, app default not loaded + assertFalse(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)); + + mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); + + assertTrue(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)); + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)) + .isEqualTo(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)); + } + + @Test + public void testGetUserMinAspectRatioEntry_enabledFullscreenOverride_returnsFullscreen() { + setIsOverrideToFullscreenEnabled(true); + // Fullscreen option is pre-selected + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_fullscreen")); + + // App default exists + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_APP_DEFAULT, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_app_default")); + } + + @Test + public void testGetUserMinAspectRatioEntry_disabledFullscreenOverride_returnsUnchanged() { + setIsOverrideToFullscreenEnabled(false); + // Fullscreen option is not pre-selected + assertThat(getUserMinAspectRatioEntry(USER_MIN_ASPECT_RATIO_UNSET, mPackageName)) + .isEqualTo(ResourcesUtils.getResourcesString( + ApplicationProvider.getApplicationContext(), + "user_aspect_ratio_app_default")); + } + + @Test + public void testIsOverrideToFullscreenEnabled_returnsTrue() + throws PackageManager.NameNotFoundException { + setIsOverrideToFullscreenEnabled(true); + assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, true); + assertTrue(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_optOut_returnsFalse() + throws PackageManager.NameNotFoundException { + setIsOverrideToFullscreenEnabled(true); + mockProperty(PROPERTY_COMPAT_ALLOW_ORIENTATION_OVERRIDE, false); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_flagDisabled_returnsFalse() { + mUtils.setFullscreenCompatChange(true); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + @Test + public void testIsOverrideToFullscreenEnabled_optionDisabled_returnsFalse() { + mUtils.setFullscreenCompatChange(true); + when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)) + .thenReturn(false); + assertFalse(mUtils.isOverrideToFullscreenEnabled(mPackageName, mContext.getUserId())); + } + + private void setIsOverrideToFullscreenEnabled(boolean enabled) { + if (enabled) { + mSetFlagsRule.enableFlags(FLAG_USER_MIN_ASPECT_RATIO_APP_DEFAULT); + mUtils = new FakeUserAspectRatioManager(mContext, mIPm); + } + mUtils.setFullscreenCompatChange(enabled); + when(mUtils.hasAspectRatioOption(USER_MIN_ASPECT_RATIO_FULLSCREEN, mPackageName)) + .thenReturn(enabled); } private void enableAllDefaultAspectRatioOptions() { @@ -328,9 +432,7 @@ public class UserAspectRatioManagerTest { throws PackageManager.NameNotFoundException { PackageManager.Property prop = new PackageManager.Property( propertyName, value, mPackageName, "" /* className */); - PackageManager pm = mock(PackageManager.class); - when(mContext.getPackageManager()).thenReturn(pm); - when(pm.getProperty(propertyName, mPackageName)).thenReturn(prop); + when(mPm.getProperty(propertyName, mPackageName)).thenReturn(prop); } private void setAspectRatioSettingsBuildTimeFlagEnabled(boolean enabled) { @@ -352,4 +454,21 @@ public class UserAspectRatioManagerTest { DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER, KEY_ENABLE_USER_ASPECT_RATIO_FULLSCREEN, enabled, makeDefault); } + + private static class FakeUserAspectRatioManager extends UserAspectRatioManager { + private boolean mFullscreenCompatChange = false; + + private FakeUserAspectRatioManager(@NonNull Context context, IPackageManager pm) { + super(context, pm); + } + + @Override + boolean isFullscreenCompatChangeEnabled(String pkgName, int userId) { + return mFullscreenCompatChange; + } + + void setFullscreenCompatChange(boolean enabled) { + mFullscreenCompatChange = enabled; + } + } } From 41ba9dc658a4792ae299012f8f0d1a3561a5f72b Mon Sep 17 00:00:00 2001 From: rambowang Date: Wed, 20 Dec 2023 19:57:14 -0600 Subject: [PATCH 8/8] Support reset IMS stack in Reset Mobile Network flow Reset the IMS stack for the specified subscription with the help of API TelephonyManager#resetIms. Bug: 271921464 Test: ResetNetworkOperationBuilderTest Change-Id: I61575f18bcc300f362000dc7f4257b9d794b6b69 --- Android.bp | 1 + src/com/android/settings/ResetNetwork.java | 15 ++++-- .../android/settings/ResetNetworkRequest.java | 51 +++++++++++++++++-- .../settings/ResetSubscriptionContract.java | 6 +-- .../network/ResetNetworkOperationBuilder.java | 27 ++++++++++ .../ResetNetworkOperationBuilderTest.java | 46 ++++++++++++++++- 6 files changed, 134 insertions(+), 12 deletions(-) diff --git a/Android.bp b/Android.bp index 79df743c573..8353634e312 100644 --- a/Android.bp +++ b/Android.bp @@ -104,6 +104,7 @@ android_library { "settings-telephony-protos-lite", "statslog-settings", "androidx.test.rules", + "telephony_flags_core_java_lib", ], plugins: ["androidx.room_room-compiler-plugin"], diff --git a/src/com/android/settings/ResetNetwork.java b/src/com/android/settings/ResetNetwork.java index 80f5962d92a..8e59a7fac82 100644 --- a/src/com/android/settings/ResetNetwork.java +++ b/src/com/android/settings/ResetNetwork.java @@ -44,6 +44,7 @@ import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.internal.telephony.flags.Flags; import com.android.settings.core.InstrumentedFragment; import com.android.settings.core.SubSettingLauncher; import com.android.settings.network.ResetNetworkRestrictionViewBuilder; @@ -121,16 +122,22 @@ public class ResetNetwork extends InstrumentedFragment { void showFinalConfirmation() { Bundle args = new Bundle(); - ResetNetworkRequest request = new ResetNetworkRequest( - ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER | - ResetNetworkRequest.RESET_VPN_MANAGER - ); + // TODO(b/317276437) Simplify the logic once flag is released + int resetOptions = ResetNetworkRequest.RESET_CONNECTIVITY_MANAGER + | ResetNetworkRequest.RESET_VPN_MANAGER; + if (Flags.resetMobileNetworkSettings()) { + resetOptions |= ResetNetworkRequest.RESET_IMS_STACK; + } + ResetNetworkRequest request = new ResetNetworkRequest(resetOptions); if (mSubscriptions != null && mSubscriptions.size() > 0) { int selectedIndex = mSubscriptionSpinner.getSelectedItemPosition(); SubscriptionInfo subscription = mSubscriptions.get(selectedIndex); int subId = subscription.getSubscriptionId(); request.setResetTelephonyAndNetworkPolicyManager(subId) .setResetApn(subId); + if (Flags.resetMobileNetworkSettings()) { + request.setResetImsSubId(subId); + } } if (mEsimContainer.getVisibility() == View.VISIBLE && mEsimCheckbox.isChecked()) { request.setResetEsim(getContext().getPackageName()) diff --git a/src/com/android/settings/ResetNetworkRequest.java b/src/com/android/settings/ResetNetworkRequest.java index 40eebb0eb29..71c12b1932e 100644 --- a/src/com/android/settings/ResetNetworkRequest.java +++ b/src/com/android/settings/ResetNetworkRequest.java @@ -48,11 +48,23 @@ public class ResetNetworkRequest { /* Reset option - reset BluetoothManager */ public static final int RESET_BLUETOOTH_MANAGER = 0x10; - /* Subscription ID for not performing reset TelephonyAndNetworkPolicy or reset APN */ + /* Reset option - reset IMS stack */ + public static final int RESET_IMS_STACK = 0x20; + + /** + * Subscription ID indicates NOT resetting any of the components below: + * - TelephonyAndNetworkPolicy + * - APN + * - IMS + */ public static final int INVALID_SUBSCRIPTION_ID = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - /* Subscription ID for performing reset TelephonyAndNetworkPolicy or reset APN - on all subscriptions */ + /** + * Subscription ID indicates resetting components below for ALL subscriptions: + * - TelephonyAndNetworkPolicy + * - APN + * - IMS + */ public static final int ALL_SUBSCRIPTION_ID = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; /* Key within Bundle. To store some connectivity options for reset */ @@ -75,10 +87,14 @@ public class ResetNetworkRequest { @VisibleForTesting protected static final String KEY_APN_SUBID = "resetApnSubId"; + /** Key within Bundle. To store subscription ID for resetting IMS. */ + protected static final String KEY_RESET_IMS_SUBID = "resetImsSubId"; + private int mResetOptions = RESET_NONE; private String mResetEsimPackageName; private int mResetTelephonyManager = INVALID_SUBSCRIPTION_ID; private int mResetApn = INVALID_SUBSCRIPTION_ID; + private int mSubscriptionIdToResetIms = INVALID_SUBSCRIPTION_ID; /** * Reconstruct based on keys stored within Bundle. @@ -93,6 +109,8 @@ public class ResetNetworkRequest { mResetTelephonyManager = optionsFromBundle.getInt( KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, INVALID_SUBSCRIPTION_ID); mResetApn = optionsFromBundle.getInt(KEY_APN_SUBID, INVALID_SUBSCRIPTION_ID); + mSubscriptionIdToResetIms = optionsFromBundle.getInt(KEY_RESET_IMS_SUBID, + INVALID_SUBSCRIPTION_ID); } /** @@ -172,6 +190,29 @@ public class ResetNetworkRequest { return this; } + /** + * Get the subscription ID applied for resetting IMS. + * @return subscription ID. + * {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions. + * {@code INVALID_SUBSCRIPTION_ID} means resetting IMS + * is not part of the option within this request. + */ + public int getResetImsSubId() { + return mSubscriptionIdToResetIms; + } + + /** + * Set the subscription ID applied for resetting APN. + * @param subId is the subscription ID referenced from SubscriptionManager. + * {@code ALL_SUBSCRIPTION_ID} for applying to all subscriptions. + * {@code INVALID_SUBSCRIPTION_ID} means resetting IMS will not take place. + * @return this + */ + public ResetNetworkRequest setResetImsSubId(int subId) { + mSubscriptionIdToResetIms = subId; + return this; + } + /** * Store a copy of this request into Bundle given. * @param writeToBundle is a Bundle for storing configurations of this request. @@ -182,6 +223,7 @@ public class ResetNetworkRequest { writeToBundle.putString(KEY_ESIM_PACKAGE, mResetEsimPackageName); writeToBundle.putInt(KEY_TELEPHONY_NET_POLICY_MANAGER_SUBID, mResetTelephonyManager); writeToBundle.putInt(KEY_APN_SUBID, mResetApn); + writeToBundle.putInt(KEY_RESET_IMS_SUBID, mSubscriptionIdToResetIms); return this; } @@ -219,6 +261,9 @@ public class ResetNetworkRequest { if (mResetApn != INVALID_SUBSCRIPTION_ID) { builder.resetApn(mResetApn); } + if ((mResetOptions & RESET_IMS_STACK) != 0) { + builder.resetIms(mSubscriptionIdToResetIms); + } return builder; } } diff --git a/src/com/android/settings/ResetSubscriptionContract.java b/src/com/android/settings/ResetSubscriptionContract.java index 580e907707b..528a16def5a 100644 --- a/src/com/android/settings/ResetSubscriptionContract.java +++ b/src/com/android/settings/ResetSubscriptionContract.java @@ -23,8 +23,8 @@ import android.util.Log; import androidx.annotation.VisibleForTesting; -import java.util.concurrent.Executors; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.IntStream; @@ -53,8 +53,8 @@ public class ResetSubscriptionContract implements AutoCloseable { mContext = context; // Only keeps specific subscription ID required to perform reset operation IntStream subIdStream = IntStream.of( - resetRequest.getResetTelephonyAndNetworkPolicyManager() - , resetRequest.getResetApnSubId()); + resetRequest.getResetTelephonyAndNetworkPolicyManager(), + resetRequest.getResetApnSubId(), resetRequest.getResetImsSubId()); mResetSubscriptionIds = subIdStream.sorted().distinct() .filter(id -> SubscriptionManager.isUsableSubscriptionId(id)) .toArray(); diff --git a/src/com/android/settings/network/ResetNetworkOperationBuilder.java b/src/com/android/settings/network/ResetNetworkOperationBuilder.java index 3583d06b2b4..61f57f949df 100644 --- a/src/com/android/settings/network/ResetNetworkOperationBuilder.java +++ b/src/com/android/settings/network/ResetNetworkOperationBuilder.java @@ -33,6 +33,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; +import com.android.settings.ResetNetworkRequest; import com.android.settings.network.apn.ApnSettings; import java.util.ArrayList; @@ -212,6 +213,32 @@ public class ResetNetworkOperationBuilder { return this; } + /** + * Append a step of resetting IMS stack. + * + * @return this + */ + public ResetNetworkOperationBuilder resetIms(int subId) { + attachSystemServiceWork(Context.TELEPHONY_SERVICE, + (Consumer) tm -> { + if (subId == ResetNetworkRequest.INVALID_SUBSCRIPTION_ID) { + // Do nothing + return; + } + if (subId == ResetNetworkRequest.ALL_SUBSCRIPTION_ID) { + // Reset IMS for all slots + for (int slotIndex = 0; slotIndex < tm.getActiveModemCount(); slotIndex++) { + tm.resetIms(slotIndex); + } + } else { + // Reset IMS for the slot specified by the sucriptionId. + final int slotIndex = SubscriptionManager.getSlotIndex(subId); + tm.resetIms(slotIndex); + } + }); + return this; + } + /** * Construct a Runnable containing all operations appended. * @return Runnable diff --git a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java index 6f5440ba5a7..41b6b27471b 100644 --- a/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java +++ b/tests/unit/src/com/android/settings/network/ResetNetworkOperationBuilderTest.java @@ -16,12 +16,12 @@ package com.android.settings.network; -import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; +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.Context; import android.net.ConnectivityManager; @@ -34,6 +34,8 @@ import android.telephony.TelephonyManager; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.settings.ResetNetworkRequest; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -129,4 +131,44 @@ public class ResetNetworkOperationBuilderTest { verify(mTelephonyManager).resetSettings(); verify(mNetworkPolicyManager).factoryReset(imsi); } + + @Test + public void resetIms_performReset_whenBuildAndRun_withSingleValidSubId() { + final int subId = 1; + doReturn(mTelephonyManager).when(mTelephonyManager) + .createForSubscriptionId(anyInt()); + doReturn(mTelephonyManager).when(mContext) + .getSystemService(Context.TELEPHONY_SERVICE); + + mBuilder.resetIms(subId).build().run(); + + verify(mTelephonyManager).resetIms(anyInt()); + } + + @Test + public void resetIms_performReset_whenBuildAndRun_withInvalidSubId() { + final int subId = ResetNetworkRequest.INVALID_SUBSCRIPTION_ID; + doReturn(mTelephonyManager).when(mTelephonyManager) + .createForSubscriptionId(anyInt()); + doReturn(mTelephonyManager).when(mContext) + .getSystemService(Context.TELEPHONY_SERVICE); + + mBuilder.resetIms(subId).build().run(); + + verify(mTelephonyManager, never()).resetIms(anyInt()); + } + + @Test + public void resetIms_performReset_whenBuildAndRun_withAllValidSubId() { + final int subId = ResetNetworkRequest.ALL_SUBSCRIPTION_ID; + doReturn(mTelephonyManager).when(mTelephonyManager) + .createForSubscriptionId(anyInt()); + doReturn(mTelephonyManager).when(mContext) + .getSystemService(Context.TELEPHONY_SERVICE); + doReturn(2).when(mTelephonyManager).getActiveModemCount(); + + mBuilder.resetIms(subId).build().run(); + + verify(mTelephonyManager, times(2)).resetIms(anyInt()); + } }