Block Policies From Device Admin Targetting Q
If a device admin app targets Android Q or above, and it is not a device
owner or profile owner, throw a SecurityException if it attempts to
control the following policies:
- DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA
- DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES
- DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD
- DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD
The set of policies available to a device admin targetting Android P or below is unchanged.
Bug: 111546201
Test: com.android.server.devicepolicy.DevicePolicyManagerTest
Test: com.android.cts.devicepolicy.DeviceAdminHostSideTestApi24
Test: com.android.cts.devicepolicy.DeviceAdminHostSideTestApi29
Test: com.android.cts.devicepolicy.ManagedProfileTest
Change-Id: Idcd0b4b91ad2fa363535c718928d382c7da054d4
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 5fbe5b3..cae3cf5 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -73,12 +73,10 @@
* that the user can select, via {@link DevicePolicyManager#setPasswordQuality}
* and {@link DevicePolicyManager#setPasswordMinimumLength}.
*
- * <p>To control this policy, the device admin must have a "limit-password"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to enforce constraints on user
- * passwords.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "limit-password" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy only affects the primary user and its profiles,
+ * but not any secondary users on the device.
*/
public static final int USES_POLICY_LIMIT_PASSWORD = 0;
@@ -138,11 +136,10 @@
* A type of policy that this device admin can use: force the user to
* change their password after an administrator-defined time limit.
*
- * <p>To control this policy, the device admin must have an "expire-password"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to enforce password expiry.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have an "expire-password" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy only affects the primary user and its profiles,
+ * but not any secondary users on the device.
*/
public static final int USES_POLICY_EXPIRE_PASSWORD = 6;
@@ -157,23 +154,19 @@
/**
* A type of policy that this device admin can use: disables use of all device cameras.
*
- * <p>To control this policy, the device admin must have a "disable-camera"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to disable use of the camera.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "disable-camera" tag in the "uses-policies" section of its meta-data.
+ * If used by a device owner, the policy affects all users on the device.
*/
public static final int USES_POLICY_DISABLE_CAMERA = 8;
/**
* A type of policy that this device admin can use: disables use of keyguard features.
*
- * <p>To control this policy, the device admin must have a "disable-keyguard-features"
- * tag in the "uses-policies" section of its meta-data.
- *
- * <p>This policy is deprecated for use by a device admin. In future releases, it will
- * only be possible for a device owner or profile owner to disable use of keyguard
- * features.
+ * <p>To control this policy, the device admin must be a device owner or profile owner,
+ * and must have a "disable-keyguard-features" tag in the "uses-policies" section of its
+ * meta-data. If used by a device owner, the policy only affects the primary user and
+ * its profiles, but not any secondary users on the device.
*/
public static final int USES_POLICY_DISABLE_KEYGUARD_FEATURES = 9;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 81ac6a4..1ea1a9e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -377,6 +377,7 @@
private static final Set<String> GLOBAL_SETTINGS_WHITELIST;
private static final Set<String> GLOBAL_SETTINGS_DEPRECATED;
private static final Set<String> SYSTEM_SETTINGS_WHITELIST;
+ private static final Set<Integer> DA_DISALLOWED_POLICIES;
static {
SECURE_SETTINGS_WHITELIST = new ArraySet<>();
SECURE_SETTINGS_WHITELIST.add(Settings.Secure.DEFAULT_INPUT_METHOD);
@@ -408,6 +409,12 @@
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS);
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_BRIGHTNESS_MODE);
SYSTEM_SETTINGS_WHITELIST.add(Settings.System.SCREEN_OFF_TIMEOUT);
+
+ DA_DISALLOWED_POLICIES = new ArraySet<>();
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD);
+ DA_DISALLOWED_POLICIES.add(DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
}
/**
@@ -2607,6 +2614,9 @@
final int userId = UserHandle.getUserId(callingUid);
final DevicePolicyData policy = getUserData(userId);
ActiveAdmin admin = policy.mAdminMap.get(who);
+ final boolean isDeviceOwner = isDeviceOwner(admin.info.getComponent(), userId);
+ final boolean isProfileOwner = isProfileOwner(admin.info.getComponent(), userId);
+
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
+ " does not own the device");
@@ -2615,6 +2625,11 @@
throw new SecurityException("Admin " + admin.info.getComponent()
+ " does not own the profile");
}
+ if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) {
+ throw new SecurityException("Admin " + admin.info.getComponent()
+ + " is not a device owner or profile owner, so may not use policy: "
+ + admin.info.getTagForPolicy(reqPolicy));
+ }
throw new SecurityException("Admin " + admin.info.getComponent()
+ " did not specify uses-policy for: "
+ admin.info.getTagForPolicy(reqPolicy));
@@ -2694,7 +2709,10 @@
// DO always has the PO power.
return ownsDevice || ownsProfile;
} else {
- return admin.info.usesPolicy(reqPolicy);
+ boolean allowedToUsePolicy = ownsDevice || ownsProfile
+ || !DA_DISALLOWED_POLICIES.contains(reqPolicy)
+ || getTargetSdk(admin.info.getPackageName(), userId) < Build.VERSION_CODES.Q;
+ return allowedToUsePolicy && admin.info.usesPolicy(reqPolicy);
}
}
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 a23636c..5da2a85 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1713,24 +1713,35 @@
UserManager.DISALLOW_ADD_USER),
eq(true), eq(CAMERA_DISABLED_GLOBALLY));
reset(getServices().userManagerInternal);
+ }
- // Set up another DA and let it disable camera. Now DISALLOW_CAMERA will only be applied
- // locally.
- dpm.setCameraDisabled(admin1, false);
- reset(getServices().userManagerInternal);
+ public void testDaDisallowedPolicies_SecurityException() throws Exception {
+ mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
+ mContext.callerPermissions.add(permission.INTERACT_ACROSS_USERS_FULL);
- setUpPackageManagerForAdmin(admin2, DpmMockContext.CALLER_SYSTEM_USER_UID);
- dpm.setActiveAdmin(admin2, /* replace =*/ false, UserHandle.USER_SYSTEM);
- dpm.setCameraDisabled(admin2, true);
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_SYSTEM_USER_UID);
+ dpm.setActiveAdmin(admin1, /* replace =*/ false, UserHandle.USER_SYSTEM);
- verify(getServices().userManagerInternal).setDevicePolicyUserRestrictions(
- eq(UserHandle.USER_SYSTEM),
- // DISALLOW_CAMERA will be applied to both local and global. <- TODO: fix this
- MockUtils.checkUserRestrictions(UserManager.DISALLOW_FUN,
- UserManager.DISALLOW_ADD_USER),
- eq(true), eq(CAMERA_DISABLED_LOCALLY));
- reset(getServices().userManagerInternal);
- // TODO Make sure restrictions are written to the file.
+ boolean originalCameraDisabled = dpm.getCameraDisabled(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setCameraDisabled(admin1, true));
+ assertEquals(originalCameraDisabled, dpm.getCameraDisabled(admin1));
+
+ int originalKeyguardDisabledFeatures = dpm.getKeyguardDisabledFeatures(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setKeyguardDisabledFeatures(admin1,
+ DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL));
+ assertEquals(originalKeyguardDisabledFeatures, dpm.getKeyguardDisabledFeatures(admin1));
+
+ long originalPasswordExpirationTimeout = dpm.getPasswordExpirationTimeout(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordExpirationTimeout(admin1, 1234));
+ assertEquals(originalPasswordExpirationTimeout, dpm.getPasswordExpirationTimeout(admin1));
+
+ int originalPasswordQuality = dpm.getPasswordQuality(admin1);
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.setPasswordQuality(admin1, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC));
+ assertEquals(originalPasswordQuality, dpm.getPasswordQuality(admin1));
}
public void testSetUserRestriction_asPo() {