From 552dbbc142324408ab8d53cfb5c82a9395f9532c 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 Merged-In: I4279dc37f0e17aedb1c2a87468478248443a253e (cherry picked from commit 269fcb6873dee199dd8023831f882aafff1f6291) --- .../server/usage/AppStandbyInternal.java | 2 + .../server/usage/AppStandbyController.java | 40 +++++++++++++++++++ .../app/admin/DevicePolicyManager.java | 3 +- .../app/usage/UsageStatsManagerInternal.java | 10 +++++ .../DevicePolicyManagerService.java | 6 ++- .../usage/AppStandbyControllerTests.java | 38 ++++++++++++++++++ .../server/usage/UsageStatsService.java | 5 +++ 7 files changed, 102 insertions(+), 2 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 e15f0f37fc62e..0e98bf0e8e004 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -146,6 +146,8 @@ public interface AppStandbyInternal { void setActiveAdminApps(Set adminPkgs, int userId); + void setAdminProtectedPackages(Set packageNames, int userId); + void onAdminDataAvailable(); void clearCarrierPrivilegedApps(); 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 0cadbfd23dbad..c9e026d73894d 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -233,6 +233,10 @@ public class AppStandbyController implements AppStandbyInternal { @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 declared activities, enabled or * disabled). Presence in this map indicates that the app is a headless system app. @@ -1020,6 +1024,9 @@ public class AppStandbyController implements AppStandbyInternal { synchronized (mActiveAdminApps) { mActiveAdminApps.remove(userId); } + synchronized (mAdminProtectedPackages) { + mAdminProtectedPackages.remove(userId); + } } } @@ -1109,6 +1116,10 @@ public class AppStandbyController implements AppStandbyInternal { return STANDBY_BUCKET_EXEMPTED; } + if (isAdminProtectedPackages(packageName, userId)) { + return STANDBY_BUCKET_EXEMPTED; + } + if (isActiveNetworkScorer(packageName)) { return STANDBY_BUCKET_EXEMPTED; } @@ -1507,6 +1518,17 @@ public class AppStandbyController implements AppStandbyInternal { } } + 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) { @@ -1530,6 +1552,17 @@ public class AppStandbyController implements AppStandbyInternal { } } + @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(); @@ -1552,6 +1585,13 @@ public class AppStandbyController implements AppStandbyInternal { } } + @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 c72234f8fea49..3768b0ce19910 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -11990,7 +11990,8 @@ public class DevicePolicyManager { /** * Called by Device owner to disable user control over apps. User will not be able to clear - * app data or force-stop packages. + * app data or force-stop packages. Packages with user control disabled are exempted from + * App Standby Buckets. * * @param admin which {@link DeviceAdminReceiver} this request is associated with * @param packages The package names for the apps. diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java index fa84427ac0645..920fe1b624175 100644 --- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -198,6 +198,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 68bc1418829fd..beb1fcdb9672c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4013,7 +4013,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { updateMaximumTimeToLockLocked(userHandle); updateLockTaskPackagesLocked(policy.mLockTaskPackages, userHandle); updateLockTaskFeaturesLocked(policy.mLockTaskFeatures, userHandle); - updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + if (userHandle == UserHandle.USER_SYSTEM) { + updateUserControlDisabledPackagesLocked(policy.mUserControlDisabledPackages); + } if (policy.mStatusBarDisabled) { setStatusBarDisabledInternal(policy.mStatusBarDisabled, userHandle); } @@ -4041,6 +4043,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void updateUserControlDisabledPackagesLocked(List packages) { mInjector.getPackageManagerInternal().setDeviceOwnerProtectedPackages(packages); + mUsageStatsManagerInternal.setAdminProtectedPackages( + new ArraySet(packages), UserHandle.USER_ALL); } private void updateLockTaskFeaturesLocked(int flags, 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 fcd3a51a9635f..2932f69fa6e10 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -123,6 +123,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; @@ -1356,6 +1359,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 public void testUserInteraction_CrossProfile() throws Exception { mInjector.mRunningUsers = new int[] {USER_ID, USER_ID2, USER_ID3}; @@ -1561,6 +1577,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 fd68ec334d8b7..131ca52ee6183 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2287,6 +2287,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();