Merge "Add privileged APIs to lock device and reset device password"
diff --git a/api/system-current.txt b/api/system-current.txt
index d29b216..79bbace 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -91,6 +91,7 @@
field public static final java.lang.String KILL_UID = "android.permission.KILL_UID";
field public static final java.lang.String LOCAL_MAC_ADDRESS = "android.permission.LOCAL_MAC_ADDRESS";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
+ field public static final java.lang.String LOCK_DEVICE = "android.permission.LOCK_DEVICE";
field public static final java.lang.String LOOP_RADIO = "android.permission.LOOP_RADIO";
field public static final java.lang.String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final java.lang.String MANAGE_APP_OPS_RESTRICTIONS = "android.permission.MANAGE_APP_OPS_RESTRICTIONS";
@@ -155,6 +156,7 @@
field public static final java.lang.String REGISTER_CONNECTION_MANAGER = "android.permission.REGISTER_CONNECTION_MANAGER";
field public static final java.lang.String REGISTER_SIM_SUBSCRIPTION = "android.permission.REGISTER_SIM_SUBSCRIPTION";
field public static final java.lang.String REMOVE_DRM_CERTIFICATES = "android.permission.REMOVE_DRM_CERTIFICATES";
+ field public static final java.lang.String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
field public static final java.lang.String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 814973d..1df3f7f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3054,6 +3054,18 @@
<permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to reset the device password.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.RESET_PASSWORD"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- @SystemApi Allows an app to lock the device.
+ <p>Not for use by third-party applications.
+ @hide -->
+ <permission android:name="android.permission.LOCK_DEVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @SystemApi Allows low-level access to setting the orientation (actually
rotation) of the screen.
<p>Not for use by third-party applications.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 0ca0835..4c71d65 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2588,12 +2588,32 @@
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(who,
+ reqPolicy, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
final int callingUid = mInjector.binderGetCallingUid();
ActiveAdmin result = getActiveAdminWithPolicyForUidLocked(who, reqPolicy, callingUid);
if (result != null) {
return result;
+ } else if (permission != null
+ && (mContext.checkCallingPermission(permission)
+ == PackageManager.PERMISSION_GRANTED)) {
+ return null;
}
if (who != null) {
@@ -2605,7 +2625,7 @@
if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
- + " does not own the device");
+ + " does not own the device");
}
if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
throw new SecurityException("Admin " + admin.info.getComponent()
@@ -2621,20 +2641,39 @@
+ admin.info.getTagForPolicy(reqPolicy));
} else {
throw new SecurityException("No active admin owned by uid "
- + mInjector.binderGetCallingUid() + " for policy #" + reqPolicy);
+ + callingUid + " for policy #" + reqPolicy);
}
}
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy, boolean parent)
throws SecurityException {
+ return getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, parent, /* permission= */ null);
+ }
+
+ /**
+ * Finds an active admin for the caller then checks {@code permission} if admin check failed.
+ *
+ * @return an active admin or {@code null} if there is no active admin but
+ * {@code permission} is granted
+ * @throws SecurityException if caller neither has an active admin nor {@code permission}
+ */
+ @Nullable
+ ActiveAdmin getActiveAdminOrCheckPermissionForCallerLocked(
+ ComponentName who,
+ int reqPolicy,
+ boolean parent,
+ @Nullable String permission) throws SecurityException {
ensureLocked();
if (parent) {
enforceManagedProfile(mInjector.userHandleGetCallingUserId(),
"call APIs on the parent profile");
}
- ActiveAdmin admin = getActiveAdminForCallerLocked(who, reqPolicy);
+ ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ who, reqPolicy, permission);
return parent ? admin.getParentActiveAdmin() : admin;
}
+
/**
* Find the admin for the component and userId bit of the uid, then check
* the admin's uid matches the uid.
@@ -4744,10 +4783,15 @@
preN = getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
} else {
- // Otherwise, make sure the caller has any active admin with the right policy.
- admin = getActiveAdminForCallerLocked(null,
- DeviceAdminInfo.USES_POLICY_RESET_PASSWORD);
- preN = getTargetSdk(admin.info.getPackageName(),
+ // Otherwise, make sure the caller has any active admin with the right policy or
+ // the required permission.
+ admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_RESET_PASSWORD,
+ android.Manifest.permission.RESET_PASSWORD);
+ // Cannot be preN if admin is null because an exception would have been
+ // thrown before getting here
+ preN = admin == null ? false : getTargetSdk(admin.info.getPackageName(),
userHandle) <= android.os.Build.VERSION_CODES.M;
// As of N, password resetting to empty/null is not allowed anymore.
@@ -4763,9 +4807,9 @@
// As of N, password cannot be changed by the admin if it is already set.
if (isLockScreenSecureUnchecked(userHandle)) {
if (!preN) {
- throw new SecurityException("Admin cannot change current password");
+ throw new SecurityException("Cannot change current password");
} else {
- Slog.e(LOG_TAG, "Admin cannot change current password");
+ Slog.e(LOG_TAG, "Cannot change current password");
return false;
}
}
@@ -5136,31 +5180,37 @@
final int callingUserId = mInjector.userHandleGetCallingUserId();
synchronized (getLockObject()) {
- // This API can only be called by an active device admin,
- // so try to retrieve it to check that the caller is one.
- final ActiveAdmin admin = getActiveAdminForCallerLocked(
- null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK, parent);
-
+ // Make sure the caller has any active admin with the right policy or
+ // the required permission.
+ final ActiveAdmin admin = getActiveAdminOrCheckPermissionForCallerLocked(
+ null,
+ DeviceAdminInfo.USES_POLICY_FORCE_LOCK,
+ parent,
+ android.Manifest.permission.LOCK_DEVICE);
final long ident = mInjector.binderClearCallingIdentity();
try {
- final ComponentName adminComponent = admin.info.getComponent();
- // Evict key
- if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
- enforceManagedProfile(
- callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
- if (!isProfileOwner(adminComponent, callingUserId)) {
- throw new SecurityException("Only profile owner admins can set "
- + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ final ComponentName adminComponent = admin == null ?
+ null : admin.info.getComponent();
+ if (adminComponent != null) {
+ // For Profile Owners only, callers with only permission not allowed.
+ if ((flags & DevicePolicyManager.FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY) != 0) {
+ // Evict key
+ enforceManagedProfile(
+ callingUserId, "set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ if (!isProfileOwner(adminComponent, callingUserId)) {
+ throw new SecurityException("Only profile owner admins can set "
+ + "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY");
+ }
+ if (parent) {
+ throw new IllegalArgumentException(
+ "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
+ }
+ if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+ throw new UnsupportedOperationException(
+ "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
+ }
+ mUserManager.evictCredentialEncryptionKey(callingUserId);
}
- if (parent) {
- throw new IllegalArgumentException(
- "Cannot set FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY for the parent");
- }
- if (!mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
- throw new UnsupportedOperationException(
- "FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY only applies to FBE devices");
- }
- mUserManager.evictCredentialEncryptionKey(callingUserId);
}
// Lock all users unless this is a managed profile with a separate challenge
@@ -5179,7 +5229,7 @@
mInjector.getTrustManager().setDeviceLockedForUser(userToLock, true);
}
- if (SecurityLog.isLoggingEnabled()) {
+ if (SecurityLog.isLoggingEnabled() && adminComponent != null) {
final int affectedUserId =
parent ? getProfileParentId(callingUserId) : callingUserId;
SecurityLog.writeEvent(SecurityLog.TAG_REMOTE_LOCK,