From 269fcb6873dee199dd8023831f882aafff1f6291 Mon Sep 17 00:00:00 2001 From: Alex Johnston Date: Wed, 8 Mar 2023 22:28:28 +0000 Subject: [PATCH] Enforce DevicePolicyManager.setUserControlDisabledPackages in AppStandbyController When deciding an app's standby bucket, check if the app has its user control disabled by an IT admin. If so, the app should be the exempted restricted bucket. Bug: 272042183 Test: atest AppStandbyControllerTests Change-Id: I4279dc37f0e17aedb1c2a87468478248443a253e --- .../server/usage/AppStandbyInternal.java | 2 + .../server/usage/AppStandbyController.java | 40 +++++++++++++++++++ .../app/admin/DevicePolicyManager.java | 5 +-- .../app/usage/UsageStatsManagerInternal.java | 10 +++++ .../DevicePolicyManagerService.java | 2 + .../usage/AppStandbyControllerTests.java | 38 ++++++++++++++++++ .../server/usage/UsageStatsService.java | 5 +++ 7 files changed, 99 insertions(+), 3 deletions(-) diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index 9b64edf53d8cf..f50a902480300 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -225,6 +225,8 @@ public interface AppStandbyInternal { void setActiveAdminApps(Set adminPkgs, int userId); + void setAdminProtectedPackages(Set packageNames, int userId); + /** * @return {@code true} if the given package is an active device admin app. */ diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index a3d566bca6b66..a013864158c72 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -268,6 +268,10 @@ public class AppStandbyController @GuardedBy("mActiveAdminApps") private final SparseArray> mActiveAdminApps = new SparseArray<>(); + /** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */ + @GuardedBy("mAdminProtectedPackages") + private final SparseArray> mAdminProtectedPackages = new SparseArray<>(); + /** * Set of system apps that are headless (don't have any "front door" activities, enabled or * disabled). Presence in this map indicates that the app is a headless system app. @@ -1380,6 +1384,9 @@ public class AppStandbyController synchronized (mActiveAdminApps) { mActiveAdminApps.remove(userId); } + synchronized (mAdminProtectedPackages) { + mAdminProtectedPackages.remove(userId); + } } } @@ -1469,6 +1476,10 @@ public class AppStandbyController return STANDBY_BUCKET_EXEMPTED; } + if (isAdminProtectedPackages(packageName, userId)) { + return STANDBY_BUCKET_EXEMPTED; + } + if (isActiveNetworkScorer(packageName)) { return STANDBY_BUCKET_EXEMPTED; } @@ -1948,6 +1959,17 @@ public class AppStandbyController } } + private boolean isAdminProtectedPackages(String packageName, int userId) { + synchronized (mAdminProtectedPackages) { + if (mAdminProtectedPackages.contains(UserHandle.USER_ALL) + && mAdminProtectedPackages.get(UserHandle.USER_ALL).contains(packageName)) { + return true; + } + return mAdminProtectedPackages.contains(userId) + && mAdminProtectedPackages.get(userId).contains(packageName); + } + } + @Override public void addActiveDeviceAdmin(String adminPkg, int userId) { synchronized (mActiveAdminApps) { @@ -1971,6 +1993,17 @@ public class AppStandbyController } } + @Override + public void setAdminProtectedPackages(Set packageNames, int userId) { + synchronized (mAdminProtectedPackages) { + if (packageNames == null || packageNames.isEmpty()) { + mAdminProtectedPackages.remove(userId); + } else { + mAdminProtectedPackages.put(userId, packageNames); + } + } + } + @Override public void onAdminDataAvailable() { mAdminDataAvailableLatch.countDown(); @@ -1993,6 +2026,13 @@ public class AppStandbyController } } + @VisibleForTesting + Set getAdminProtectedPackagesForTest(int userId) { + synchronized (mAdminProtectedPackages) { + return mAdminProtectedPackages.get(userId); + } + } + /** * Returns {@code true} if the supplied package is the device provisioning app. Otherwise, * returns {@code false}. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0241417776045..27ee28ad14e65 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -15818,9 +15818,8 @@ public class DevicePolicyManager { * Called by a device owner or a profile owner or holder of the permission * {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APPS_CONTROL} to disable user * control over apps. User will not be able to clear app data or force-stop packages. When - * called by a device owner, applies to all users on the device. Starting from Android 13, - * packages with user control disabled are exempted from being put in the "restricted" App - * Standby Bucket. + * called by a device owner, applies to all users on the device. Packages with user control + * disabled are exempted from App Standby Buckets. * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the * caller is not a device admin. diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index 70eeb7fecc8fa..fc565111dbe8e 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -202,6 +202,16 @@ public abstract class UsageStatsManagerInternal { */ public abstract void setActiveAdminApps(Set adminApps, int userId); + /** + * Called by DevicePolicyManagerService to inform about the protected packages for a user. + * User control will be disabled for protected packages. + * + * @param packageNames the set of protected packages for {@code userId}. + * @param userId the userId to which the protected packages belong. + */ + public abstract void setAdminProtectedPackages(@Nullable Set packageNames, + @UserIdInt int userId); + /** * Called by DevicePolicyManagerService during boot to inform that admin data is loaded and * pushed to UsageStatsService. diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 400ee1d3baf35..4147aebb4bab4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -3554,6 +3554,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.binderWithCleanCallingIdentity(() -> mInjector.getPackageManagerInternal().setOwnerProtectedPackages( targetUserId, protectedPackages)); + mUsageStatsManagerInternal.setAdminProtectedPackages(new ArraySet(protectedPackages), + targetUserId); } void handleUnlockUser(int userId) { diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 9570ff6323d28..86878c5384fdb 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -160,6 +160,9 @@ public class AppStandbyControllerTests { private static final String ADMIN_PKG2 = "com.android.admin2"; private static final String ADMIN_PKG3 = "com.android.admin3"; + private static final String ADMIN_PROTECTED_PKG = "com.android.admin.protected"; + private static final String ADMIN_PROTECTED_PKG2 = "com.android.admin.protected2"; + private static final long MINUTE_MS = 60 * 1000; private static final long HOUR_MS = 60 * MINUTE_MS; private static final long DAY_MS = 24 * HOUR_MS; @@ -1757,6 +1760,19 @@ public class AppStandbyControllerTests { assertIsNotActiveAdmin(ADMIN_PKG2, USER_ID); } + @Test + public void testSetAdminProtectedPackages() { + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID, ADMIN_PROTECTED_PKG, ADMIN_PROTECTED_PKG2); + assertAdminProtectedPackagesForTest(USER_ID2, (String[]) null); + + setAdminProtectedPackages(USER_ID, (String[]) null); + assertAdminProtectedPackagesForTest(USER_ID, (String[]) null); + } + @Test @FlakyTest(bugId = 185169504) public void testUserInteraction_CrossProfile() throws Exception { @@ -2195,6 +2211,28 @@ public class AppStandbyControllerTests { mController.setActiveAdminApps(new ArraySet<>(Arrays.asList(admins)), userId); } + private void setAdminProtectedPackages(int userId, String... packageNames) { + Set adminProtectedPackages = packageNames != null ? new ArraySet<>( + Arrays.asList(packageNames)) : null; + mController.setAdminProtectedPackages(adminProtectedPackages, userId); + } + + private void assertAdminProtectedPackagesForTest(int userId, String... packageNames) { + final Set actualAdminProtectedPackages = + mController.getAdminProtectedPackagesForTest(userId); + if (packageNames == null) { + if (actualAdminProtectedPackages != null && !actualAdminProtectedPackages.isEmpty()) { + fail("Admin protected packages should be null; " + getAdminAppsStr(userId, + actualAdminProtectedPackages)); + } + return; + } + assertEquals(packageNames.length, actualAdminProtectedPackages.size()); + for (String adminProtectedPackage : packageNames) { + assertTrue(actualAdminProtectedPackages.contains(adminProtectedPackage)); + } + } + private void setAndAssertBucket(String pkg, int user, int bucket, int reason) throws Exception { rearmLatch(pkg); mController.setAppStandbyBucket(pkg, user, bucket, reason); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 7ff5b4a28f1bd..8948e494d8511 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -3128,6 +3128,11 @@ public class UsageStatsService extends SystemService implements mAppStandby.setActiveAdminApps(packageNames, userId); } + @Override + public void setAdminProtectedPackages(Set packageNames, int userId) { + mAppStandby.setAdminProtectedPackages(packageNames, userId); + } + @Override public void onAdminDataAvailable() { mAppStandby.onAdminDataAvailable();