Exempt device lock controller and kiosk roles from metered restrictions.
Bug: 352771686
Test: atest FrameworksServicesTests:DevicePolicyManagerTest
Flag: EXEMPT bugfix
Change-Id: I84741fbb34ee17ef421790d1a3340a2f9412c376
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d5013517..38ccb7e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -871,6 +871,16 @@
EXEMPT_FROM_POWER_RESTRICTIONS, OPSTR_SYSTEM_EXEMPT_FROM_POWER_RESTRICTIONS);
}
+ private static final Set<String> METERED_DATA_RESTRICTION_EXEMPT_ROLES =
+ new ArraySet<>();
+ static {
+ // TODO(b/362545319): reference role name from role manager once it's exposed.
+ final String roleDeviceLockController =
+ "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+ METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(roleDeviceLockController);
+ METERED_DATA_RESTRICTION_EXEMPT_ROLES.add(RoleManager.ROLE_FINANCED_DEVICE_KIOSK);
+ }
+
/**
* Admin apps targeting Android S+ may not use
* {@link android.app.admin.DevicePolicyManager#setPasswordQuality} to set password quality
@@ -1959,6 +1969,10 @@
return UserManager.isHeadlessSystemUserMode();
}
+ List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return getRoleManager().getRoleHoldersAsUser(role, userHandle);
+ }
+
@SuppressWarnings("AndroidFrameworkPendingIntentMutability")
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
@NonNull Intent intent, int flags, Bundle options, UserHandle user) {
@@ -17920,15 +17934,28 @@
});
}
+ private Set<String> getMeteredDataRestrictionExemptPackages(int userId) {
+ final Set<String> exemptPkgs = new ArraySet<>();
+ for (String role: METERED_DATA_RESTRICTION_EXEMPT_ROLES) {
+ String pkg = getRoleHolderPackageNameOnUser(role, userId);
+ if (pkg != null) {
+ exemptPkgs.add(pkg);
+ }
+ }
+
+ return exemptPkgs;
+ }
+
private List<String> removeInvalidPkgsForMeteredDataRestriction(
int userId, List<String> pkgNames) {
+ final Set<String> exemptRolePkgs = getMeteredDataRestrictionExemptPackages(userId);
synchronized (getLockObject()) {
final Set<String> activeAdmins = getActiveAdminPackagesLocked(userId);
final List<String> excludedPkgs = new ArrayList<>();
for (int i = pkgNames.size() - 1; i >= 0; --i) {
final String pkgName = pkgNames.get(i);
- // If the package is an active admin, don't restrict it.
- if (activeAdmins.contains(pkgName)) {
+ // If the package is an active admin or exempt role, don't restrict it.
+ if (activeAdmins.contains(pkgName) || exemptRolePkgs.contains(pkgName)) {
excludedPkgs.add(pkgName);
continue;
}
@@ -21839,8 +21866,6 @@
*/
@Nullable
private String getRoleHolderPackageNameOnUser(String role, int userId) {
- RoleManager roleManager = mContext.getSystemService(RoleManager.class);
-
// Clear calling identity as the RoleManager APIs require privileged permissions.
return mInjector.binderWithCleanCallingIdentity(() -> {
List<UserInfo> users;
@@ -21852,7 +21877,7 @@
}
for (UserInfo user : users) {
List<String> roleHolders =
- roleManager.getRoleHoldersAsUser(role, user.getUserHandle());
+ mInjector.roleManagerGetRoleHoldersAsUser(role, user.getUserHandle());
if (!roleHolders.isEmpty()) {
return roleHolders.get(0);
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index b4cc343..698bda3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -60,6 +60,7 @@
import java.io.File;
import java.io.IOException;
+import java.util.List;
import java.util.Map;
/**
@@ -325,6 +326,11 @@
}
@Override
+ List<String> roleManagerGetRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return services.roleManagerForMock.getRoleHoldersAsUser(role, userHandle);
+ }
+
+ @Override
PendingIntent pendingIntentGetActivityAsUser(Context context, int requestCode,
Intent intent, int flags, Bundle options, UserHandle user) {
return null;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index b7483d6..cb4269a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -109,6 +109,7 @@
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.SystemUpdatePolicy;
import android.app.admin.WifiSsidPolicy;
+import android.app.role.RoleManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
@@ -2889,6 +2890,52 @@
}
@Test
+ public void testSetMeteredDataDisabledPackagesExemptRoles() throws Exception {
+ // TODO(b/362545319): reference role name from role manager once it's exposed.
+ final String controllerRole = "android.app.role.SYSTEM_FINANCED_DEVICE_CONTROLLER";
+
+ setAsProfileOwner(admin1);
+
+ assertThat(dpm.getMeteredDataDisabledPackages(admin1)).isEmpty();
+
+ // Setup
+ final ArrayList<String> pkgsToRestrict = new ArrayList<>();
+ final ArrayList<String> pkgsExpectedAsNotRestricted = new ArrayList<>();
+ final String packageWithControllerRole = "com.example.controller";
+ final String packageWithKioskRole = "com.example.kiosk";
+ final String packageWithNotExemptRole = "com.example.notexempt";
+
+ pkgsToRestrict.add(packageWithControllerRole);
+ pkgsToRestrict.add(packageWithKioskRole);
+ pkgsToRestrict.add(packageWithNotExemptRole);
+
+ pkgsExpectedAsNotRestricted.add(packageWithControllerRole);
+ pkgsExpectedAsNotRestricted.add(packageWithKioskRole);
+
+ setupPackageInPackageManager(packageWithControllerRole, CALLER_USER_HANDLE, 123, 0);
+ setupPackageInPackageManager(packageWithKioskRole, CALLER_USER_HANDLE, 456, 0);
+ setupPackageInPackageManager(packageWithNotExemptRole, CALLER_USER_HANDLE, 789, 0);
+
+ when(getServices().roleManagerForMock.getRoleHoldersAsUser(controllerRole,
+ UserHandle.of(CALLER_USER_HANDLE)))
+ .thenReturn(new ArrayList<>(Arrays.asList(packageWithControllerRole)));
+ when(getServices().roleManagerForMock.getRoleHoldersAsUser(
+ RoleManager.ROLE_FINANCED_DEVICE_KIOSK,
+ UserHandle.of(CALLER_USER_HANDLE)))
+ .thenReturn(new ArrayList<>(Arrays.asList(packageWithKioskRole)));
+
+ List<String> excludedPkgs = dpm.setMeteredDataDisabledPackages(admin1, pkgsToRestrict);
+
+ // Verify
+ assertThat(excludedPkgs).containsExactlyElementsIn(pkgsExpectedAsNotRestricted);
+ assertThat(dpm.getMeteredDataDisabledPackages(admin1))
+ .isEqualTo(Arrays.asList(packageWithNotExemptRole));
+ verify(getServices().networkPolicyManagerInternal).setMeteredRestrictedPackages(
+ MockUtils.checkApps(packageWithNotExemptRole),
+ eq(CALLER_USER_HANDLE));
+ }
+
+ @Test
public void testSetGetMeteredDataDisabledPackages_deviceAdmin() {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
dpm.setActiveAdmin(admin1, true);
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index 76aa40c..2e200a9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -142,6 +142,7 @@
public final DevicePolicyManager devicePolicyManager;
public final LocationManager locationManager;
public final RoleManager roleManager;
+ public final RoleManagerForMock roleManagerForMock;
public final SubscriptionManager subscriptionManager;
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
@@ -200,6 +201,7 @@
devicePolicyManager = mock(DevicePolicyManager.class);
locationManager = mock(LocationManager.class);
roleManager = realContext.getSystemService(RoleManager.class);
+ roleManagerForMock = mock(RoleManagerForMock.class);
subscriptionManager = mock(SubscriptionManager.class);
// Package manager is huge, so we use a partial mock instead.
@@ -495,6 +497,12 @@
}
}
+ public static class RoleManagerForMock {
+ public List<String> getRoleHoldersAsUser(String role, UserHandle userHandle) {
+ return new ArrayList<>();
+ }
+ }
+
public static class SettingsForMock {
public int settingsSecureGetIntForUser(String name, int def, int userHandle) {
return 0;