Create public API for policy transparency outside of Settings
The new DPM.createAdminSupportIntent() returns an intent that shows the
"This action was disabled by your admin"-dialog from settings.
This enables apps to inform the user about the cause of restricted
functionality.
A new extra for the intent allows to specialize the dialog for different
restricted features, instead of a generic message for all features.
Bug: 31215663
Test: runtest -c com.android.server.devicepolicy.DevicePolicyManagerTest frameworks-services
Change-Id: I3de7aeec0f88b8f013a63957aec803cd123fbedc
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7558e3c..5c15750 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -8957,18 +8957,20 @@
ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
if (profileOwner != null) {
- return createShowAdminSupportIntent(profileOwner, userId);
+ return DevicePolicyManagerService.this
+ .createShowAdminSupportIntent(profileOwner, userId);
}
final Pair<Integer, ComponentName> deviceOwner =
mOwners.getDeviceOwnerUserIdAndComponent();
if (deviceOwner != null && deviceOwner.first == userId) {
- return createShowAdminSupportIntent(deviceOwner.second, userId);
+ return DevicePolicyManagerService.this
+ .createShowAdminSupportIntent(deviceOwner.second, userId);
}
// We're not specifying the device admin because there isn't one.
if (useDefaultIfNoAdmin) {
- return createShowAdminSupportIntent(null, userId);
+ return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
}
return null;
}
@@ -8996,11 +8998,12 @@
if (enforcedByDo && enforcedByPo) {
// In this case, we'll show an admin support dialog that does not
// specify the admin.
- return createShowAdminSupportIntent(null, userId);
+ return DevicePolicyManagerService.this.createShowAdminSupportIntent(null, userId);
} else if (enforcedByPo) {
final ComponentName profileOwner = mOwners.getProfileOwnerComponent(userId);
if (profileOwner != null) {
- return createShowAdminSupportIntent(profileOwner, userId);
+ return DevicePolicyManagerService.this
+ .createShowAdminSupportIntent(profileOwner, userId);
}
// This could happen if another thread has changed the profile owner since we called
// getUserRestrictionSource
@@ -9009,7 +9012,8 @@
final Pair<Integer, ComponentName> deviceOwner
= mOwners.getDeviceOwnerUserIdAndComponent();
if (deviceOwner != null) {
- return createShowAdminSupportIntent(deviceOwner.second, deviceOwner.first);
+ return DevicePolicyManagerService.this
+ .createShowAdminSupportIntent(deviceOwner.second, deviceOwner.first);
}
// This could happen if another thread has changed the device owner since we called
// getUserRestrictionSource
@@ -9017,15 +9021,57 @@
}
return null;
}
+ }
- private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
- // This method is called with AMS lock held, so don't take DPMS lock
- final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
- intent.putExtra(Intent.EXTRA_USER_ID, userId);
- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- return intent;
+ private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
+ // This method is called with AMS lock held, so don't take DPMS lock
+ final Intent intent = new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS);
+ intent.putExtra(Intent.EXTRA_USER_ID, userId);
+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, admin);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return intent;
+ }
+
+ @Override
+ public Intent createAdminSupportIntent(String restriction) {
+ Preconditions.checkNotNull(restriction);
+ final int uid = mInjector.binderGetCallingUid();
+ final int userId = UserHandle.getUserId(uid);
+ Intent intent = null;
+ if (DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction) ||
+ DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction)) {
+ synchronized(this) {
+ final DevicePolicyData policy = getUserData(userId);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ final ActiveAdmin admin = policy.mAdminList.get(i);
+ if ((admin.disableCamera &&
+ DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) ||
+ (admin.disableScreenCapture && DevicePolicyManager
+ .POLICY_DISABLE_SCREEN_CAPTURE.equals(restriction))) {
+ intent = createShowAdminSupportIntent(admin.info.getComponent(), userId);
+ break;
+ }
+ }
+ // For the camera, a device owner on a different user can disable it globally,
+ // so we need an additional check.
+ if (intent == null
+ && DevicePolicyManager.POLICY_DISABLE_CAMERA.equals(restriction)) {
+ final ActiveAdmin admin = getDeviceOwnerAdminLocked();
+ if (admin != null && admin.disableCamera) {
+ intent = createShowAdminSupportIntent(admin.info.getComponent(),
+ mOwners.getDeviceOwnerUserId());
+ }
+ }
+ }
+ } else {
+ // if valid, |restriction| can only be a user restriction
+ intent = mLocalService.createUserRestrictionSupportIntent(userId, restriction);
}
+ if (intent != null) {
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, restriction);
+ }
+ return intent;
}
/**
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 5d4c3cf..c29668f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1950,6 +1950,81 @@
}
}
+ public void testCreateAdminSupportIntent() throws Exception {
+ // Setup device owner.
+ mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+ setupDeviceOwner();
+
+ // Nonexisting permission returns null
+ Intent intent = dpm.createAdminSupportIntent("disallow_nothing");
+ assertNull(intent);
+
+ // Existing permission that is not set returns null
+ intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+ assertNull(intent);
+
+ // Existing permission that is not set by device/profile owner returns null
+ when(mContext.userManager.hasUserRestriction(
+ eq(UserManager.DISALLOW_ADJUST_VOLUME),
+ eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+ .thenReturn(true);
+ intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+ assertNull(intent);
+
+ // Permission that is set by device owner returns correct intent
+ when(mContext.userManager.getUserRestrictionSource(
+ eq(UserManager.DISALLOW_ADJUST_VOLUME),
+ eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
+ .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ intent = dpm.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+ assertNotNull(intent);
+ assertEquals(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS, intent.getAction());
+ assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID),
+ intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+ assertEquals(admin1,
+ (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+ assertEquals(UserManager.DISALLOW_ADJUST_VOLUME,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+ // Try with POLICY_DISABLE_CAMERA and POLICY_DISABLE_SCREEN_CAPTURE, which are not
+ // user restrictions
+
+ // Camera is not disabled
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+ assertNull(intent);
+
+ // Camera is disabled
+ dpm.setCameraDisabled(admin1, true);
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+ assertNotNull(intent);
+ assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+ // Screen capture is not disabled
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+ assertNull(intent);
+
+ // Screen capture is disabled
+ dpm.setScreenCaptureDisabled(admin1, true);
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+ assertNotNull(intent);
+ assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+ // Same checks for different user
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ // Camera should be disabled by device owner
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_CAMERA);
+ assertNotNull(intent);
+ assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA,
+ intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+ assertEquals(UserHandle.getUserId(DpmMockContext.CALLER_SYSTEM_USER_UID),
+ intent.getIntExtra(Intent.EXTRA_USER_ID, -1));
+ // ScreenCapture should not be disabled by device owner
+ intent = dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+ assertNull(intent);
+ }
+
/**
* Test for:
* {@link DevicePolicyManager#setAffiliationIds}