Add test method to remove admins.
Add test method to remove admins that declare
FLAG_TEST_APP without informing them.
The method will also remove the device and profile
owner status of the admin.
Bug: 28027468
Change-Id: Idb4d3299a9c6595c94bfb424546cd8a384131835
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index b83484d..31c7421 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -44,6 +44,7 @@
private static final String COMMAND_SET_ACTIVE_ADMIN = "set-active-admin";
private static final String COMMAND_SET_DEVICE_OWNER = "set-device-owner";
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
+ private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -60,6 +61,8 @@
"[ --name <NAME> ] <COMPONENT>\n" +
"usage: dpm set-profile-owner [ --user <USER_ID> | current ] [ --name <NAME> ] " +
"<COMPONENT>\n" +
+ "usage: dpm remove-active-admin [ --user <USER_ID> | current ] [ --name <NAME> ] " +
+ "<COMPONENT>\n" +
"\n" +
"dpm set-active-admin: Sets the given component as active admin" +
" for an existing user.\n" +
@@ -68,7 +71,11 @@
" package as device owner.\n" +
"\n" +
"dpm set-profile-owner: Sets the given component as active admin and profile" +
- " owner for an existing user.\n");
+ " owner for an existing user.\n" +
+ "\n" +
+ "dpm remove-active-admin: Disables an active admin, the admin must have declared" +
+ " android:testOnly in the application in its manifest. This will also remove" +
+ " device and profile owners\n");
}
@Override
@@ -91,6 +98,9 @@
case COMMAND_SET_PROFILE_OWNER:
runSetProfileOwner();
break;
+ case COMMAND_REMOVE_ACTIVE_ADMIN:
+ runRemoveActiveAdmin();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
@@ -152,6 +162,12 @@
System.out.println("Active admin set to component " + mComponent.toShortString());
}
+ private void runRemoveActiveAdmin() throws RemoteException {
+ parseArgs(/*canHaveName=*/ false);
+ mDevicePolicyManager.forceRemoveActiveAdmin(mComponent, mUserId);
+ System.out.println("Success: Admin removed " + mComponent);
+ }
+
private void runSetProfileOwner() throws RemoteException {
parseArgs(/*canHaveName=*/ true);
mDevicePolicyManager.setActiveAdmin(mComponent, true /*refreshing*/, mUserId);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 7a18df6..0ca2e14 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6391,6 +6391,24 @@
}
}
+ /**
+ * @hide
+ * Remove a test admin synchronously without sending it a broadcast about being removed.
+ * If the admin is a profile owner or device owner it will still be removed.
+ *
+ * @param userHandle user id to remove the admin for.
+ * @param admin The administration compononent to remove.
+ * @throws SecurityException if the caller is not shell / root or the admin package
+ * isn't a test application see {@link ApplicationInfo#FLAG_TEST_APP}.
+ */
+ public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) {
+ try {
+ mService.forceRemoveActiveAdmin(adminReceiver, userHandle);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
private void throwIfParentInstance(String functionName) {
if (mParentInstance) {
throw new SecurityException(functionName + " cannot be called on the parent instance");
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index cba64c2..989e613 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -111,6 +111,7 @@
boolean packageHasActiveAdmins(String packageName, int userHandle);
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result, int userHandle);
void removeActiveAdmin(in ComponentName policyReceiver, int userHandle);
+ void forceRemoveActiveAdmin(in ComponentName policyReceiver, int userHandle);
boolean hasGrantedPolicy(in ComponentName policyReceiver, int usesPolicy, int userHandle);
void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d3d05f3..45a7311 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2909,6 +2909,54 @@
}
}
+ public void forceRemoveActiveAdmin(ComponentName adminReceiver, int userHandle) {
+ if (!mHasFeature) {
+ return;
+ }
+ Preconditions.checkNotNull(adminReceiver, "ComponentName is null");
+ enforceShell("forceRemoveActiveAdmin");
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ final ApplicationInfo ai;
+ try {
+ ai = mIPackageManager.getApplicationInfo(adminReceiver.getPackageName(),
+ 0, userHandle);
+ } catch (RemoteException e) {
+ throw new IllegalStateException(e);
+ }
+ if (ai == null) {
+ throw new IllegalStateException("Couldn't find package to remove admin "
+ + adminReceiver.getPackageName() + " " + userHandle);
+ }
+ if ((ai.flags & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
+ throw new SecurityException("Attempt to remove non-test admin " + adminReceiver
+ + adminReceiver + " " + userHandle);
+ }
+ // If admin is a device or profile owner tidy that up first.
+ synchronized (this) {
+ if (isDeviceOwner(adminReceiver, userHandle)) {
+ clearDeviceOwnerLocked(getDeviceOwnerAdminLocked(), userHandle);
+ }
+ if (isProfileOwner(adminReceiver, userHandle)) {
+ final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver,
+ userHandle, /* parent */ false);
+ clearProfileOwnerLocked(admin, userHandle);
+ }
+ }
+ // Remove the admin skipping sending the broadcast.
+ removeAdminArtifacts(adminReceiver, userHandle);
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
+ }
+ }
+
+ private void enforceShell(String method) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SHELL_UID && callingUid != Process.ROOT_UID) {
+ throw new SecurityException("Non-shell user attempted to call " + method);
+ }
+ }
+
@Override
public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) {
if (!mHasFeature) {
@@ -5732,32 +5780,37 @@
enforceUserUnlocked(deviceOwnerUserId);
final ActiveAdmin admin = getDeviceOwnerAdminLocked();
- if (admin != null) {
- admin.disableCamera = false;
- admin.userRestrictions = null;
- admin.forceEphemeralUsers = false;
- mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers);
- }
- clearUserPoliciesLocked(deviceOwnerUserId);
-
- mOwners.clearDeviceOwner();
- mOwners.writeDeviceOwner();
- updateDeviceOwnerLocked();
- disableSecurityLoggingIfNotCompliant();
- // Reactivate backup service.
long ident = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
+ clearDeviceOwnerLocked(admin, deviceOwnerUserId);
removeActiveAdminLocked(deviceOwnerComponent, deviceOwnerUserId);
- } catch (RemoteException e) {
- throw new IllegalStateException("Failed reactivating backup service.", e);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
}
}
}
+ private void clearDeviceOwnerLocked(ActiveAdmin admin, int userId) {
+ if (admin != null) {
+ admin.disableCamera = false;
+ admin.userRestrictions = null;
+ admin.forceEphemeralUsers = false;
+ mUserManagerInternal.setForceEphemeralUsers(admin.forceEphemeralUsers);
+ }
+ clearUserPoliciesLocked(userId);
+
+ mOwners.clearDeviceOwner();
+ mOwners.writeDeviceOwner();
+ updateDeviceOwnerLocked();
+ disableSecurityLoggingIfNotCompliant();
+ try {
+ // Reactivate backup service.
+ mInjector.getIBackupManager().setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed reactivating backup service.", e);
+ }
+ }
+
@Override
public boolean setProfileOwner(ComponentName who, String ownerName, int userHandle) {
if (!mHasFeature) {
@@ -5794,14 +5847,9 @@
final ActiveAdmin admin =
getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
synchronized (this) {
- admin.disableCamera = false;
- admin.userRestrictions = null;
- clearUserPoliciesLocked(userId);
- mOwners.removeProfileOwner(userId);
- mOwners.writeProfileOwner(userId);
-
final long ident = mInjector.binderClearCallingIdentity();
try {
+ clearProfileOwnerLocked(admin, userId);
removeActiveAdminLocked(who, userId);
} finally {
mInjector.binderRestoreCallingIdentity(ident);
@@ -5809,6 +5857,16 @@
}
}
+ public void clearProfileOwnerLocked(ActiveAdmin admin, int userId) {
+ if (admin != null) {
+ admin.disableCamera = false;
+ admin.userRestrictions = null;
+ }
+ clearUserPoliciesLocked(userId);
+ mOwners.removeProfileOwner(userId);
+ mOwners.writeProfileOwner(userId);
+ }
+
@Override
public void setDeviceOwnerLockScreenInfo(ComponentName who, CharSequence info) {
Preconditions.checkNotNull(who, "ComponentName is null");
@@ -5842,15 +5900,13 @@
policy.mUserProvisioningState = DevicePolicyManager.STATE_USER_UNMANAGED;
saveSettingsLocked(userId);
- final long ident = mInjector.binderClearCallingIdentity();
try {
mIPackageManager.updatePermissionFlagsForAllApps(
PackageManager.FLAG_PERMISSION_POLICY_FIXED,
0 /* flagValues */, userId);
pushUserRestrictions(userId);
} catch (RemoteException re) {
- } finally {
- mInjector.binderRestoreCallingIdentity(ident);
+ // Shouldn't happen.
}
}