From 05f85914d8285f53c548891707ffb8d24b50336a Mon Sep 17 00:00:00 2001 From: Hani Kazmi Date: Thu, 2 Nov 2023 17:49:06 +0000 Subject: [PATCH 1/8] Enable ECM restrictions for Usage Access and Device Admin This commit continues the work to make all special app access permissions ECM restrictable. Some implementation notes: 1. The FilterTouchesSwitchPreference and AppSwitchPrefernce components are replaced with RestrictedSwitchPreference. afaict this is a superset - it still filters out obscured touches and shows the app icon. 2. I'm treating this as mostly a refactoring, and so do not have a feature flag around most of the changes. Enabling ECM for them /is/ behind the feature flag in RestrictedLockUtilsInternal. 3. app_ops_permissions_details.xml is currently only used by UsageAccessDetails. Bug: 297372999 Test: Manually tested on device. Automated tests to follow Change-Id: I65fe7ec099582de19192a77ad2e41c1558761502 --- res/xml/app_ops_permissions_details.xml | 2 +- .../applications/UsageAccessDetails.java | 16 ++++++++++---- .../deviceadmin/DeviceAdminListItem.java | 8 +++++++ .../DeviceAdminListPreferenceController.java | 22 +++++++++++-------- 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/res/xml/app_ops_permissions_details.xml b/res/xml/app_ops_permissions_details.xml index eb8188bad6b..3cbe009fc06 100644 --- a/res/xml/app_ops_permissions_details.xml +++ b/res/xml/app_ops_permissions_details.xml @@ -17,7 +17,7 @@ - { return new UserHandle(getUserIdFromDeviceAdminInfo(mInfo)); } + public int getUid() { + return mInfo.getActivityInfo().applicationInfo.uid; + } + + public String getPackageName() { + return mInfo.getPackageName(); + } + public Intent getLaunchIntent(Context context) { return new Intent(context, DeviceAdminAdd.class) .putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mInfo.getComponent()); diff --git a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java index 1184d8e41c5..55ba8acafad 100644 --- a/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java +++ b/src/com/android/settings/applications/specialaccess/deviceadmin/DeviceAdminListPreferenceController.java @@ -18,6 +18,7 @@ package com.android.settings.applications.specialaccess.deviceadmin; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED; +import android.Manifest; import android.app.AppGlobals; import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminReceiver; @@ -45,12 +46,13 @@ import androidx.preference.PreferenceScreen; import com.android.settings.core.BasePreferenceController; import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.core.lifecycle.LifecycleObserver; import com.android.settingslib.core.lifecycle.events.OnStart; import com.android.settingslib.core.lifecycle.events.OnStop; -import com.android.settingslib.widget.AppSwitchPreference; import com.android.settingslib.widget.FooterPreference; +import com.android.settingslib.widget.TwoTargetPreference; import org.xmlpull.v1.XmlPullParserException; @@ -167,35 +169,35 @@ public class DeviceAdminListPreferenceController extends BasePreferenceControlle if (mFooterPreference != null) { mFooterPreference.setVisible(mAdmins.isEmpty()); } - final Map preferenceCache = new ArrayMap<>(); + final Map preferenceCache = new ArrayMap<>(); final Context prefContext = mPreferenceGroup.getContext(); final int childrenCount = mPreferenceGroup.getPreferenceCount(); for (int i = 0; i < childrenCount; i++) { final Preference pref = mPreferenceGroup.getPreference(i); - if (!(pref instanceof AppSwitchPreference)) { + if (!(pref instanceof RestrictedSwitchPreference switchPref)) { continue; } - final AppSwitchPreference appSwitch = (AppSwitchPreference) pref; - preferenceCache.put(appSwitch.getKey(), appSwitch); + preferenceCache.put(switchPref.getKey(), switchPref); } for (DeviceAdminListItem item : mAdmins) { final String key = item.getKey(); - AppSwitchPreference pref = preferenceCache.remove(key); + RestrictedSwitchPreference pref = preferenceCache.remove(key); if (pref == null) { - pref = new AppSwitchPreference(prefContext); + pref = new RestrictedSwitchPreference(prefContext); mPreferenceGroup.addPreference(pref); } bindPreference(item, pref); } - for (AppSwitchPreference unusedCacheItem : preferenceCache.values()) { + for (RestrictedSwitchPreference unusedCacheItem : preferenceCache.values()) { mPreferenceGroup.removePreference(unusedCacheItem); } } - private void bindPreference(DeviceAdminListItem item, AppSwitchPreference pref) { + private void bindPreference(DeviceAdminListItem item, RestrictedSwitchPreference pref) { pref.setKey(item.getKey()); pref.setTitle(item.getName()); pref.setIcon(item.getIcon()); + pref.setIconSize(TwoTargetPreference.ICON_SIZE_DEFAULT); pref.setChecked(item.isActive()); pref.setSummary(item.getDescription()); pref.setEnabled(item.isEnabled()); @@ -207,6 +209,8 @@ public class DeviceAdminListPreferenceController extends BasePreferenceControlle }); pref.setOnPreferenceChangeListener((preference, newValue) -> false); pref.setSingleLineTitle(true); + pref.checkEcmRestrictionAndSetDisabled(Manifest.permission.BIND_DEVICE_ADMIN, + item.getPackageName(), item.getUid()); } /** From c4ec19acfa3a34c91befde1de8ee13a4533a77d3 Mon Sep 17 00:00:00 2001 From: Jan Tomljanovic Date: Thu, 21 Dec 2023 13:54:03 +0000 Subject: [PATCH 2/8] Protect "Lock after timeout" page with an auth challenge. Test: manual Bug: 315937886 Change-Id: Ib3da9f783892828c0c9cfdc4ebb27b2d982e5274 --- ...ettings_security_flag_declarations.aconfig | 8 ++++ res/xml/screen_lock_settings.xml | 2 +- .../ProtectedTimeoutListPreference.java | 40 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 aconfig/settings_security_flag_declarations.aconfig create mode 100644 src/com/android/settings/security/screenlock/ProtectedTimeoutListPreference.java diff --git a/aconfig/settings_security_flag_declarations.aconfig b/aconfig/settings_security_flag_declarations.aconfig new file mode 100644 index 00000000000..42ef4d03e5b --- /dev/null +++ b/aconfig/settings_security_flag_declarations.aconfig @@ -0,0 +1,8 @@ +package: "com.android.settings.flags" + +flag { + name: "protect_lock_after_timeout_with_auth" + namespace: "safety_center" + description: "Require an auth challenge to open Lock after timeout page" + bug: "315937886" +} diff --git a/res/xml/screen_lock_settings.xml b/res/xml/screen_lock_settings.xml index 3f732b957dd..19061d9b855 100644 --- a/res/xml/screen_lock_settings.xml +++ b/res/xml/screen_lock_settings.xml @@ -40,7 +40,7 @@ - Date: Wed, 10 Jan 2024 21:55:16 +0000 Subject: [PATCH 3/8] Adapt clickShortcutSettingsPreference* test to shortcut edit activity. This failure appeared when rolling out flag cleanup_accessibility_warning_dialog. This test used to always launch the editor dialog, but that logic has changed with the rollout of another feature to replace the dialog with an activity launch. Bug: 319497827 Test: ToggleAccessibilityServicePreferenceFragmentTest Change-Id: Ie4e3ae5eff8ed16cded622ce71bbd3a6a55cdedb --- ...sibilityServicePreferenceFragmentTest.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java index 8cdb17ea5a6..9bcd327d776 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragmentTest.java @@ -19,8 +19,10 @@ package com.android.settings.accessibility; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.accessibilityservice.AccessibilityServiceInfo; @@ -33,6 +35,7 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Bundle; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -45,7 +48,9 @@ import androidx.preference.PreferenceScreen; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.SettingsActivity; import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType; +import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment; import com.android.settings.widget.SettingsMainSwitchPreference; import org.junit.Before; @@ -53,6 +58,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -298,7 +304,9 @@ public class ToggleAccessibilityServicePreferenceFragmentTest { @Test @RequiresFlagsEnabled(Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG) - public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning() + @RequiresFlagsDisabled( + com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN) + public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_showDialog() throws Throwable { setupServiceWarningRequired(false); mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); @@ -309,6 +317,23 @@ public class ToggleAccessibilityServicePreferenceFragmentTest { AccessibilityDialogUtils.DialogEnums.EDIT_SHORTCUT); } + @Test + @RequiresFlagsEnabled({Flags.FLAG_CLEANUP_ACCESSIBILITY_WARNING_DIALOG, + com.android.settings.accessibility.Flags.FLAG_EDIT_SHORTCUTS_IN_FULL_SCREEN}) + public void clickShortcutSettingsPreference_warningNotRequired_dontShowWarning_launchActivity() + throws Throwable { + setupServiceWarningRequired(false); + mFragment.mShortcutPreference = new ShortcutPreference(mContext, /* attrs= */null); + doNothing().when(mContext).startActivity(any()); + + mFragment.onSettingsClicked(mFragment.mShortcutPreference); + + ArgumentCaptor captor = ArgumentCaptor.forClass(Intent.class); + verify(mContext).startActivity(captor.capture()); + assertThat(captor.getValue().getExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT)) + .isEqualTo(EditShortcutsPreferenceFragment.class.getName()); + } + private void setupTileService(String packageName, String name, String tileName) { final Intent tileProbe = new Intent(TileService.ACTION_QS_TILE); final ResolveInfo info = new ResolveInfo(); From 1ce7938afc7747986a001ba1345f95e817042194 Mon Sep 17 00:00:00 2001 From: Fan Wu Date: Mon, 8 Jan 2024 18:20:53 +0800 Subject: [PATCH 4/8] Update custom shadow usage of ShadowActivityManager Test: atest SettingsRoboTests Bug: 319052511 Change-Id: I15ee6a19d039808aab5089e309a872cf6bc818bc --- ...istenerScreenPreferenceControllerTest.java | 6 ++--- .../zenaccess/ZenAccessControllerTest.java | 12 +++------ .../AdvancedPowerUsageDetailTest.java | 16 ++++++----- .../PowerBackgroundUsageDetailTest.java | 16 ++++++----- .../SettingsHomepageActivityTest.java | 19 +++++++------ ...eNotificationPreferenceControllerTest.java | 27 ++++++++++--------- ...yNotificationPreferenceControllerTest.java | 22 +++++++-------- .../app/BubblePreferenceControllerTest.java | 13 +++++---- ...BubbleSummaryPreferenceControllerTest.java | 26 +++++++++--------- 9 files changed, 79 insertions(+), 78 deletions(-) diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/vrlistener/VrListenerScreenPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/vrlistener/VrListenerScreenPreferenceControllerTest.java index 1e5e235e1cf..7405980ffda 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/vrlistener/VrListenerScreenPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/vrlistener/VrListenerScreenPreferenceControllerTest.java @@ -23,15 +23,15 @@ import static com.google.common.truth.Truth.assertThat; import android.content.Context; -import com.android.settings.testutils.shadow.ShadowActivityManager; +import androidx.test.core.app.ApplicationProvider; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = { @@ -45,7 +45,7 @@ public class VrListenerScreenPreferenceControllerTest { @Before public void setUp() { - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mController = new VrListenerScreenPreferenceController(mContext, "key"); mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE)); } diff --git a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java index bf112a6c255..badc5dbb0bd 100644 --- a/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java +++ b/tests/robotests/src/com/android/settings/applications/specialaccess/zenaccess/ZenAccessControllerTest.java @@ -25,23 +25,20 @@ import static org.mockito.Mockito.verify; import android.app.NotificationManager; import android.content.Context; +import androidx.test.core.app.ApplicationProvider; + import com.android.internal.logging.nano.MetricsProto; import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowNotificationManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; @RunWith(RobolectricTestRunner.class) -@Config(shadows = { - ShadowActivityManager.class, -}) public class ZenAccessControllerTest { private static final String TEST_PKG = "com.test.package"; @@ -49,15 +46,12 @@ public class ZenAccessControllerTest { private FakeFeatureFactory mFeatureFactory; private Context mContext; private ZenAccessController mController; - private ShadowActivityManager mActivityManager; - @Before public void setUp() { - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mFeatureFactory = FakeFeatureFactory.setupForTest(); mController = new ZenAccessController(mContext, "key"); - mActivityManager = Shadow.extract(mContext.getSystemService(Context.ACTIVITY_SERVICE)); } @Test diff --git a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java index c6a9a718d69..583db5e1d50 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/AdvancedPowerUsageDetailTest.java @@ -46,6 +46,7 @@ import android.os.UserHandle; 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; @@ -53,7 +54,6 @@ 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.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.PrimarySwitchPreference; @@ -65,15 +65,16 @@ import com.android.settingslib.widget.LayoutPreference; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; @@ -83,10 +84,13 @@ import java.util.concurrent.TimeUnit; @Config( shadows = { ShadowEntityHeaderController.class, - ShadowActivityManager.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class AdvancedPowerUsageDetailTest { + + @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"}; @@ -125,9 +129,7 @@ public class AdvancedPowerUsageDetailTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); - - mContext = spy(RuntimeEnvironment.application); + mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getPackageName()).thenReturn("foo"); mFeatureFactory = FakeFeatureFactory.setupForTest(); mMetricsFeatureProvider = mFeatureFactory.metricsFeatureProvider; diff --git a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java index 6edbafacf9a..b6caa7f2061 100644 --- a/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java +++ b/tests/robotests/src/com/android/settings/fuelgauge/PowerBackgroundUsageDetailTest.java @@ -47,11 +47,11 @@ import android.widget.CompoundButton; import androidx.fragment.app.FragmentActivity; import androidx.loader.app.LoaderManager; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.SettingsActivity; import com.android.settings.fuelgauge.batteryusage.BatteryEntry; import com.android.settings.testutils.FakeFeatureFactory; -import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowEntityHeaderController; import com.android.settings.widget.EntityHeaderController; import com.android.settingslib.applications.AppUtils; @@ -65,15 +65,16 @@ import com.android.settingslib.widget.SelectorWithWidgetPreference; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.mockito.stubbing.Answer; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; @@ -83,10 +84,13 @@ import java.util.concurrent.TimeUnit; @Config( shadows = { ShadowEntityHeaderController.class, - ShadowActivityManager.class, com.android.settings.testutils.shadow.ShadowFragment.class, }) public class PowerBackgroundUsageDetailTest { + + @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; @@ -123,9 +127,7 @@ public class PowerBackgroundUsageDetailTest { @Before public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mContext = spy(RuntimeEnvironment.application); + mContext = spy(ApplicationProvider.getApplicationContext()); when(mContext.getPackageName()).thenReturn("foo"); when(mContext.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.getInstallSourceInfo(anyString())).thenReturn(mInstallSourceInfo); diff --git a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java index 27a85fd3d4e..6cd4f167692 100644 --- a/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java +++ b/tests/robotests/src/com/android/settings/homepage/SettingsHomepageActivityTest.java @@ -45,29 +45,30 @@ import android.view.WindowManager; import android.widget.FrameLayout; import androidx.fragment.app.Fragment; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.dashboard.suggestions.SuggestionFeatureProviderImpl; import com.android.settings.testutils.shadow.ShadowActivityEmbeddingUtils; -import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settings.testutils.shadow.ShadowPasswordUtils; import com.android.settings.testutils.shadow.ShadowUserManager; import com.android.settingslib.core.lifecycle.HideNonSystemOverlayMixin; import org.junit.After; -import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.Robolectric; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; import org.robolectric.util.ReflectionHelpers; @RunWith(RobolectricTestRunner.class) @@ -77,11 +78,8 @@ import org.robolectric.util.ReflectionHelpers; ShadowActivityManager.class, }) public class SettingsHomepageActivityTest { - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - } + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); @After public void tearDown() { @@ -120,7 +118,8 @@ public class SettingsHomepageActivityTest { @Config(qualifiers = "mcc999") public void launch_LowRamDevice_shouldHideAvatar() { final ShadowActivityManager activityManager = Shadow.extract( - RuntimeEnvironment.application.getSystemService(ActivityManager.class)); + ApplicationProvider.getApplicationContext().getSystemService( + ActivityManager.class)); activityManager.setIsLowRamDevice(true); final SettingsHomepageActivity activity = Robolectric.buildActivity( diff --git a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java index b2759767112..ea0b2a06d2f 100644 --- a/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/BubbleNotificationPreferenceControllerTest.java @@ -34,21 +34,23 @@ import android.provider.Settings; import android.widget.Switch; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; -import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor; import com.android.settingslib.widget.MainSwitchPreference; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = { @@ -56,7 +58,10 @@ import org.robolectric.shadow.api.Shadow; ShadowActivityManager.class, }) public class BubbleNotificationPreferenceControllerTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; private Context mContext; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private PreferenceScreen mScreen; @@ -67,33 +72,29 @@ public class BubbleNotificationPreferenceControllerTest { private BubbleNotificationPreferenceController mController; private MainSwitchPreference mPreference; - private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; + private ShadowActivityManager mActivityManager; @Before public void setUp() { - MockitoAnnotations.initMocks(this); - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mController = new BubbleNotificationPreferenceController(mContext, KEY_NOTIFICATION_BUBBLES); - mPreference = new MainSwitchPreference(RuntimeEnvironment.application); + mPreference = new MainSwitchPreference(mContext); mPreference.setKey(mController.getPreferenceKey()); when(mScreen.findPreference(mPreference.getKey())).thenReturn(mPreference); mController.displayPreference(mScreen); + mActivityManager = Shadow.extract(mContext.getSystemService(ActivityManager.class)); } @Test public void isAvailable_lowRam_returnsUnsupported() { - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(true); + mActivityManager.setIsLowRamDevice(true); assertEquals(UNSUPPORTED_ON_DEVICE, mController.getAvailabilityStatus()); } @Test public void isAvailable_notLowRam_returnsAvailable() { - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(false); + mActivityManager.setIsLowRamDevice(false); assertEquals(AVAILABLE, mController.getAvailabilityStatus()); } diff --git a/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java index e80fbc839d4..a201fdb4cb9 100644 --- a/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/BubbleSummaryNotificationPreferenceControllerTest.java @@ -30,17 +30,17 @@ import android.content.Context; import android.provider.Settings; import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; -import com.android.settings.testutils.shadow.ShadowActivityManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = { @@ -48,19 +48,23 @@ import org.robolectric.shadow.api.Shadow; }) public class BubbleSummaryNotificationPreferenceControllerTest { + private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; private Context mContext; private BubbleSummaryNotificationPreferenceController mController; private Preference mPreference; - private static final String KEY_NOTIFICATION_BUBBLES = "notification_bubbles"; + private ShadowActivityManager mActivityManager; + @Before public void setUp() { - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); mController = new BubbleSummaryNotificationPreferenceController(mContext, KEY_NOTIFICATION_BUBBLES); - mPreference = new Preference(RuntimeEnvironment.application); + mPreference = new Preference(mContext); + mActivityManager = + Shadow.extract(mContext.getSystemService(ActivityManager.class)); } @Test @@ -80,17 +84,13 @@ public class BubbleSummaryNotificationPreferenceControllerTest { @Test public void isAvailable_lowRam_returnsUnsupported() { - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(true); + mActivityManager.setIsLowRamDevice(true); assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE); } @Test public void isAvailable_notLowRam_returnsAvailable() { - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(false); + mActivityManager.setIsLowRamDevice(false); assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); } } diff --git a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java index 487ba769972..893695cb2e4 100644 --- a/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/BubblePreferenceControllerTest.java @@ -55,24 +55,26 @@ import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.notification.NotificationBackend; -import com.android.settings.testutils.shadow.ShadowActivityManager; import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedSwitchPreference; import com.google.common.collect.ImmutableList; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowActivityManager; import org.robolectric.shadows.ShadowApplication; import java.util.ArrayList; @@ -83,6 +85,8 @@ import java.util.List; ShadowActivityManager.class, }) public class BubblePreferenceControllerTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private Context mContext; @Mock @@ -103,11 +107,10 @@ public class BubblePreferenceControllerTest { @Before public void setUp() { - MockitoAnnotations.initMocks(this); ShadowApplication shadowApplication = ShadowApplication.getInstance(); shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); shadowApplication.setSystemService(Context.USER_SERVICE, mUm); - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); when(mFragmentManager.beginTransaction()).thenReturn(mock(FragmentTransaction.class)); mController = spy(new BubblePreferenceController(mContext, mFragmentManager, mBackend, false /* isAppPage */, mListener)); diff --git a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java index 7db308e173e..6fbe40e22e0 100644 --- a/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/app/BubbleSummaryPreferenceControllerTest.java @@ -43,27 +43,30 @@ import android.content.Context; import android.provider.Settings; import androidx.preference.Preference; +import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; import com.android.settings.notification.NotificationBackend; -import com.android.settings.testutils.shadow.ShadowActivityManager; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; -import org.robolectric.shadows.ShadowApplication; +import org.robolectric.shadows.ShadowActivityManager; @RunWith(RobolectricTestRunner.class) @Config(shadows = { ShadowActivityManager.class, }) public class BubbleSummaryPreferenceControllerTest { + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); private Context mContext; @Mock @@ -72,16 +75,17 @@ public class BubbleSummaryPreferenceControllerTest { private BubbleSummaryPreferenceController mController; + private ShadowActivityManager mActivityManager; + @Before public void setUp() { - MockitoAnnotations.initMocks(this); - ShadowApplication shadowApplication = ShadowApplication.getInstance(); - mContext = RuntimeEnvironment.application; + mContext = ApplicationProvider.getApplicationContext(); when(mBackend.hasSentValidBubble(anyString(), anyInt())).thenReturn(true); mAppRow = new NotificationBackend.AppRow(); mAppRow.pkg = "pkg"; mAppRow.uid = 0; mController = spy(new BubbleSummaryPreferenceController(mContext, mBackend)); + mActivityManager = Shadow.extract(mContext.getSystemService(ActivityManager.class)); } @Test @@ -151,9 +155,7 @@ public class BubbleSummaryPreferenceControllerTest { Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); mController.onResume(mAppRow, null, null, null, null, null, null); - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(true); + mActivityManager.setIsLowRamDevice(true); assertFalse(mController.isAvailable()); } @@ -162,9 +164,7 @@ public class BubbleSummaryPreferenceControllerTest { Settings.Secure.putInt(mContext.getContentResolver(), NOTIFICATION_BUBBLES, SYSTEM_WIDE_ON); mController.onResume(mAppRow, null, null, null, null, null, null); - final ShadowActivityManager activityManager = - Shadow.extract(mContext.getSystemService(ActivityManager.class)); - activityManager.setIsLowRamDevice(false); + mActivityManager.setIsLowRamDevice(false); assertTrue(mController.isAvailable()); } From 55a7023af59565f1baaab55fa79e5036532a9c6e Mon Sep 17 00:00:00 2001 From: Wilson Wu Date: Fri, 5 Jan 2024 08:07:17 +0000 Subject: [PATCH 5/8] Add metrics for keyboard vibration settings Log the state when keyboard vibration settings changed. Bug: 289107579 Test: atest KeyboardVibrationTogglePreferenceControllerTest Change-Id: I96999fe49a86514561c85c5b589a7b90e3c3cb67 --- ...KeyboardVibrationTogglePreferenceController.java | 9 ++++++++- ...oardVibrationTogglePreferenceControllerTest.java | 13 +++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java index 858b374db75..869443c5fae 100644 --- a/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java +++ b/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceController.java @@ -21,7 +21,7 @@ import static android.provider.Settings.System.KEYBOARD_VIBRATION_ENABLED; import static com.android.settings.accessibility.AccessibilityUtil.State.OFF; import static com.android.settings.accessibility.AccessibilityUtil.State.ON; - +import android.app.settings.SettingsEnums; import android.content.Context; import android.database.ContentObserver; import android.net.Uri; @@ -42,6 +42,8 @@ import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.core.TogglePreferenceController; +import com.android.settings.overlay.FeatureFactory; +import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; /** @@ -62,6 +64,8 @@ public class KeyboardVibrationTogglePreferenceController extends TogglePreferenc @Nullable private TwoStatePreference mPreference; + private MetricsFeatureProvider mMetricsFeatureProvider; + public KeyboardVibrationTogglePreferenceController(Context context, String preferenceKey) { super(context, preferenceKey); mVibrator = context.getSystemService(Vibrator.class); @@ -75,6 +79,7 @@ public class KeyboardVibrationTogglePreferenceController extends TogglePreferenc } } }; + mMetricsFeatureProvider = FeatureFactory.getFeatureFactory().getMetricsFeatureProvider(); } @Override @@ -120,6 +125,8 @@ public class KeyboardVibrationTogglePreferenceController extends TogglePreferenc @Override public boolean setChecked(boolean isChecked) { final boolean success = updateKeyboardVibrationSetting(isChecked); + mMetricsFeatureProvider.action(mContext, + SettingsEnums.ACTION_KEYBOARD_VIBRATION_CHANGED, isChecked); if (success && isChecked) { // Play the preview vibration effect when the toggle is on. final VibrationAttributes touchAttrs = diff --git a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java index 6bf83c6126a..cf12e341337 100644 --- a/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/KeyboardVibrationTogglePreferenceControllerTest.java @@ -23,9 +23,13 @@ import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_ import static com.google.common.truth.Truth.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import android.app.settings.SettingsEnums; import android.content.Context; import android.content.res.Resources; import android.os.vibrator.Flags; @@ -37,6 +41,7 @@ import androidx.preference.SwitchPreference; import androidx.test.core.app.ApplicationProvider; import com.android.settings.R; +import com.android.settings.testutils.FakeFeatureFactory; import org.junit.Before; import org.junit.Rule; @@ -49,7 +54,6 @@ import org.robolectric.RobolectricTestRunner; /** Tests for {@link KeyboardVibrationTogglePreferenceController}. */ @RunWith(RobolectricTestRunner.class) public class KeyboardVibrationTogglePreferenceControllerTest { - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -59,8 +63,8 @@ public class KeyboardVibrationTogglePreferenceControllerTest { private Context mContext; private Resources mResources; private KeyboardVibrationTogglePreferenceController mController; - private SwitchPreference mPreference; + private FakeFeatureFactory mFeatureFactory; @Before public void setUp() { @@ -68,6 +72,7 @@ public class KeyboardVibrationTogglePreferenceControllerTest { mContext = spy(ApplicationProvider.getApplicationContext()); mResources = spy(mContext.getResources()); when(mContext.getResources()).thenReturn(mResources); + mFeatureFactory = FakeFeatureFactory.setupForTest(); mController = new KeyboardVibrationTogglePreferenceController(mContext, "preferenceKey"); mPreference = new SwitchPreference(mContext); when(mPreferenceScreen.findPreference( @@ -148,6 +153,8 @@ public class KeyboardVibrationTogglePreferenceControllerTest { mController.setChecked(true); assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(ON); + verify(mFeatureFactory.metricsFeatureProvider).action(any(), + eq(SettingsEnums.ACTION_KEYBOARD_VIBRATION_CHANGED), eq(true)); } @Test @@ -160,6 +167,8 @@ public class KeyboardVibrationTogglePreferenceControllerTest { mController.setChecked(false); assertThat(readSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED)).isEqualTo(OFF); + verify(mFeatureFactory.metricsFeatureProvider).action(any(), + eq(SettingsEnums.ACTION_KEYBOARD_VIBRATION_CHANGED), eq(false)); } private void updateSystemSetting(String key, int value) { From ff74d9db0b65fd9e5f5da2b80156fc788b09358e Mon Sep 17 00:00:00 2001 From: shaoweishen Date: Mon, 8 Jan 2024 08:33:01 +0000 Subject: [PATCH 6/8] [Physical Keyboard Setting] Add log and null check update getParcelable api usage, add null check and logging. Bug: 317416208 Test: Verified on device Change-Id: I125a6c9cdb5c86be7582ad5102257efb1fa6a1a4 --- .../NewKeyboardLayoutEnabledLocalesFragment.java | 8 +++++++- .../settings/inputmethod/PhysicalKeyboardFragment.java | 9 ++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java index abe640bcadb..05dc5bea33a 100644 --- a/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java +++ b/src/com/android/settings/inputmethod/NewKeyboardLayoutEnabledLocalesFragment.java @@ -105,8 +105,13 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment public void onActivityCreated(final Bundle icicle) { super.onActivityCreated(icicle); Bundle arguments = getArguments(); + if (arguments == null) { + Log.e(TAG, "Arguments should not be null"); + return; + } mInputDeviceIdentifier = - arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER); + arguments.getParcelable(NewKeyboardSettingsUtils.EXTRA_INPUT_DEVICE_IDENTIFIER, + InputDeviceIdentifier.class); if (mInputDeviceIdentifier == null) { Log.e(TAG, "The inputDeviceIdentifier should not be null"); return; @@ -128,6 +133,7 @@ public class NewKeyboardLayoutEnabledLocalesFragment extends DashboardFragment InputDevice inputDevice = NewKeyboardSettingsUtils.getInputDevice(mIm, mInputDeviceIdentifier); if (inputDevice == null) { + Log.e(TAG, "Unable to start: input device is null"); getActivity().finish(); return; } diff --git a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java index 38de93ea2d2..7de505edbca 100644 --- a/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java +++ b/src/com/android/settings/inputmethod/PhysicalKeyboardFragment.java @@ -151,7 +151,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment mKeyboardA11yCategory.removePreference(mAccessibilityStickyKeys); } InputDeviceIdentifier inputDeviceIdentifier = activity.getIntent().getParcelableExtra( - KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER); + KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER, + InputDeviceIdentifier.class); int intentFromWhere = activity.getIntent().getIntExtra(android.provider.Settings.EXTRA_ENTRYPOINT, -1); if (intentFromWhere != -1) { @@ -168,7 +169,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment } private static boolean isAutoSelection(Bundle bundle, InputDeviceIdentifier identifier) { - if (bundle != null && bundle.getParcelable(EXTRA_AUTO_SELECTION) != null) { + if (bundle != null && bundle.getParcelable(EXTRA_AUTO_SELECTION, + InputDeviceIdentifier.class) != null) { return false; } return identifier != null; @@ -446,7 +448,8 @@ public final class PhysicalKeyboardFragment extends SettingsPreferenceFragment if (mIntentWaitingForResult != null) { InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult - .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER); + .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER, + InputDeviceIdentifier.class); mIntentWaitingForResult = null; showKeyboardLayoutDialog(inputDeviceIdentifier); } From 52ebf58566ec643196b4aa85bc8d0f4aa9615b78 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Thu, 11 Jan 2024 17:02:55 +0800 Subject: [PATCH 7/8] Show policy transparent of "Force stop" when no DO PackageManager.isPackageStateProtected() is moving to permission based, so force stop button can also be protected when no DeviceOwner. The ActivityManger side has already handled correctly, no Settings UI side need to show a device policy transparent dialog. Fix: 319579347 Test: manual - Force stop on AppInfo Test: unit test Change-Id: I4432dcb798a0cfcbb6bfc8b30c9191dd91b980a2 --- .../spa/app/appinfo/AppForceStopButton.kt | 6 +- .../spa/app/appinfo/AppForceStopButtonTest.kt | 141 ++++++++++++------ 2 files changed, 98 insertions(+), 49 deletions(-) diff --git a/src/com/android/settings/spa/app/appinfo/AppForceStopButton.kt b/src/com/android/settings/spa/app/appinfo/AppForceStopButton.kt index 345d931f9c0..c3183a7494f 100644 --- a/src/com/android/settings/spa/app/appinfo/AppForceStopButton.kt +++ b/src/com/android/settings/spa/app/appinfo/AppForceStopButton.kt @@ -19,6 +19,7 @@ package com.android.settings.spa.app.appinfo import android.app.settings.SettingsEnums import android.content.pm.ApplicationInfo import android.os.UserManager +import androidx.annotation.VisibleForTesting import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Report import androidx.compose.material3.Text @@ -87,9 +88,10 @@ class AppForceStopButton( dialogPresenter.open() } - private fun getAdminRestriction(app: ApplicationInfo): EnforcedAdmin? = when { + @VisibleForTesting + fun getAdminRestriction(app: ApplicationInfo): EnforcedAdmin? = when { packageManager.isPackageStateProtected(app.packageName, app.userId) -> { - RestrictedLockUtilsInternal.getDeviceOwner(context) + RestrictedLockUtilsInternal.getDeviceOwner(context) ?: EnforcedAdmin() } else -> RestrictedLockUtilsInternal.checkIfRestrictionEnforced( diff --git a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppForceStopButtonTest.kt b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppForceStopButtonTest.kt index c093863bcb1..84d6651c396 100644 --- a/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppForceStopButtonTest.kt +++ b/tests/spa_unit/src/com/android/settings/spa/app/appinfo/AppForceStopButtonTest.kt @@ -17,79 +17,82 @@ package com.android.settings.spa.app.appinfo import android.app.admin.DevicePolicyManager +import android.content.ComponentName import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.PackageManager +import android.os.UserHandle +import android.os.UserManager +import androidx.compose.material3.Button +import androidx.compose.material3.Text +import androidx.compose.ui.test.assertIsEnabled +import androidx.compose.ui.test.assertIsNotEnabled import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.settingslib.spa.widget.button.ActionButton +import com.android.settings.R +import com.android.settingslib.spa.testutils.delay import com.android.settingslib.spaprivileged.framework.common.devicePolicyManager import com.android.settingslib.spaprivileged.model.app.userId import com.google.common.truth.Truth.assertThat -import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Spy -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.mockito.Mockito.`when` as whenever +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class AppForceStopButtonTest { @get:Rule val composeTestRule = createComposeRule() - @get:Rule - val mockito: MockitoRule = MockitoJUnit.rule() + private val mockPackageManager = mock() - @Spy - private val context: Context = ApplicationProvider.getApplicationContext() + private val mockDevicePolicyManager = mock() - @Mock - private lateinit var packageInfoPresenter: PackageInfoPresenter - - @Mock - private lateinit var packageManager: PackageManager - - @Mock - private lateinit var devicePolicyManager: DevicePolicyManager - - private lateinit var appForceStopButton: AppForceStopButton - - @Before - fun setUp() { - whenever(packageInfoPresenter.context).thenReturn(context) - whenever(context.packageManager).thenReturn(packageManager) - whenever(context.devicePolicyManager).thenReturn(devicePolicyManager) - appForceStopButton = AppForceStopButton(packageInfoPresenter) + private val mockUserManager = mock { + on { getUserRestrictionSources(any(), any()) } doReturn emptyList() } - @Test - fun getActionButton() { + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { packageManager } doReturn mockPackageManager + on { devicePolicyManager } doReturn mockDevicePolicyManager + on { getSystemService(Context.DEVICE_POLICY_SERVICE) } doReturn mockDevicePolicyManager + on { getSystemService(Context.USER_SERVICE) } doReturn mockUserManager } + private val packageInfoPresenter = mock { + on { context } doReturn context + } + + private val appForceStopButton = AppForceStopButton(packageInfoPresenter) + @Test fun getActionButton_isActiveAdmin_buttonDisabled() { val app = createApp() - whenever(devicePolicyManager.packageHasActiveAdmins(PACKAGE_NAME, app.userId)) - .thenReturn(true) + mockDevicePolicyManager.stub { + on { packageHasActiveAdmins(PACKAGE_NAME, app.userId) } doReturn true + } - val actionButton = setForceStopButton(app) + setForceStopButton(app) - assertThat(actionButton.enabled).isFalse() + composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled() } @Test fun getActionButton_isUninstallInQueue_buttonDisabled() { val app = createApp() - whenever(devicePolicyManager.isUninstallInQueue(PACKAGE_NAME)).thenReturn(true) + mockDevicePolicyManager.stub { + on { isUninstallInQueue(PACKAGE_NAME) } doReturn true + } - val actionButton = setForceStopButton(app) + setForceStopButton(app) - assertThat(actionButton.enabled).isFalse() + composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled() } @Test @@ -98,35 +101,79 @@ class AppForceStopButtonTest { flags = ApplicationInfo.FLAG_STOPPED } - val actionButton = setForceStopButton(app) + setForceStopButton(app) - assertThat(actionButton.enabled).isFalse() + composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsNotEnabled() } @Test fun getActionButton_regularApp_buttonEnabled() { val app = createApp() - val actionButton = setForceStopButton(app) + setForceStopButton(app) - assertThat(actionButton.enabled).isTrue() + composeTestRule.onNodeWithText(context.getString(R.string.force_stop)).assertIsEnabled() } - private fun setForceStopButton(app: ApplicationInfo): ActionButton { - lateinit var actionButton: ActionButton - composeTestRule.setContent { - actionButton = appForceStopButton.getActionButton(app) + @Test + fun getAdminRestriction_packageNotProtected() { + mockPackageManager.stub { + on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn false } - return actionButton + + val admin = appForceStopButton.getAdminRestriction(createApp()) + + assertThat(admin).isNull() + } + + @Test + fun getAdminRestriction_packageProtectedAndHaveOwner() { + mockPackageManager.stub { + on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn true + } + mockDevicePolicyManager.stub { + on { deviceOwnerComponentOnAnyUser } doReturn DEVICE_OWNER + } + + val admin = appForceStopButton.getAdminRestriction(createApp())!! + + assertThat(admin.component).isEqualTo(DEVICE_OWNER) + } + + @Test + fun getAdminRestriction_packageProtectedAndNotHaveOwner() { + mockPackageManager.stub { + on { isPackageStateProtected(PACKAGE_NAME, UserHandle.getUserId(UID)) } doReturn true + } + mockDevicePolicyManager.stub { + on { deviceOwnerComponentOnAnyUser } doReturn null + } + + val admin = appForceStopButton.getAdminRestriction(createApp())!! + + assertThat(admin.component).isNull() + } + + private fun setForceStopButton(app: ApplicationInfo) { + composeTestRule.setContent { + val actionButton = appForceStopButton.getActionButton(app) + Button(onClick = {}, enabled = actionButton.enabled) { + Text(actionButton.text) + } + } + composeTestRule.delay() } private fun createApp(builder: ApplicationInfo.() -> Unit = {}) = ApplicationInfo().apply { packageName = PACKAGE_NAME + uid = UID enabled = true }.apply(builder) private companion object { const val PACKAGE_NAME = "package.name" + const val UID = 10000 + val DEVICE_OWNER = ComponentName("device", "Owner") } -} \ No newline at end of file +} From d0fcba932caee4b32d8ef1874feca9869c2386ea Mon Sep 17 00:00:00 2001 From: Edgar Wang Date: Thu, 11 Jan 2024 11:18:45 +0000 Subject: [PATCH 8/8] Fix time zone settings restriction logic - Time zone should be restricted when "no_config_date_time" enabled Bug: 316584466 Change-Id: I90dfc2c84ef0b2155740c7b890f17376c9e57e51 Test: manual via TestDPC --- .../settings/datetime/TimeZonePreferenceController.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/com/android/settings/datetime/TimeZonePreferenceController.java b/src/com/android/settings/datetime/TimeZonePreferenceController.java index 913640db16a..d45173f48cc 100644 --- a/src/com/android/settings/datetime/TimeZonePreferenceController.java +++ b/src/com/android/settings/datetime/TimeZonePreferenceController.java @@ -26,6 +26,7 @@ import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import com.android.settings.core.BasePreferenceController; +import com.android.settingslib.RestrictedPreference; import com.android.settingslib.datetime.ZoneGetter; import java.util.Calendar; @@ -52,6 +53,12 @@ public class TimeZonePreferenceController extends BasePreferenceController { @Override public void updateState(Preference preference) { super.updateState(preference); + + if (preference instanceof RestrictedPreference + && ((RestrictedPreference) preference).isDisabledByAdmin()) { + return; + } + preference.setEnabled(shouldEnableManualTimeZoneSelection()); }