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
This commit is contained in:
Alex Johnston
2023-03-08 22:28:28 +00:00
parent f2de79ebee
commit 269fcb6873
7 changed files with 99 additions and 3 deletions

View File

@@ -225,6 +225,8 @@ public interface AppStandbyInternal {
void setActiveAdminApps(Set<String> adminPkgs, int userId);
void setAdminProtectedPackages(Set<String> packageNames, int userId);
/**
* @return {@code true} if the given package is an active device admin app.
*/

View File

@@ -268,6 +268,10 @@ public class AppStandbyController
@GuardedBy("mActiveAdminApps")
private final SparseArray<Set<String>> mActiveAdminApps = new SparseArray<>();
/** List of admin protected packages. Can contain {@link android.os.UserHandle#USER_ALL}. */
@GuardedBy("mAdminProtectedPackages")
private final SparseArray<Set<String>> 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<String> 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<String> 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}.

View File

@@ -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.

View File

@@ -202,6 +202,16 @@ public abstract class UsageStatsManagerInternal {
*/
public abstract void setActiveAdminApps(Set<String> 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<String> packageNames,
@UserIdInt int userId);
/**
* Called by DevicePolicyManagerService during boot to inform that admin data is loaded and
* pushed to UsageStatsService.

View File

@@ -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) {

View File

@@ -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<String> adminProtectedPackages = packageNames != null ? new ArraySet<>(
Arrays.asList(packageNames)) : null;
mController.setAdminProtectedPackages(adminProtectedPackages, userId);
}
private void assertAdminProtectedPackagesForTest(int userId, String... packageNames) {
final Set<String> 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);

View File

@@ -3128,6 +3128,11 @@ public class UsageStatsService extends SystemService implements
mAppStandby.setActiveAdminApps(packageNames, userId);
}
@Override
public void setAdminProtectedPackages(Set<String> packageNames, int userId) {
mAppStandby.setAdminProtectedPackages(packageNames, userId);
}
@Override
public void onAdminDataAvailable() {
mAppStandby.onAdminDataAvailable();