diff --git a/api/11.xml b/api/11.xml index c7906d02d72af..9b747bb4768fb 100644 --- a/api/11.xml +++ b/api/11.xml @@ -30672,6 +30672,17 @@ + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - When multiple device administrators attempt to control device * encryption, the most secure, supported setting will always be @@ -1167,7 +1154,10 @@ public class DevicePolicyManager { * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. * @param encrypt true to request encryption, false to release any previous request - * @return current status of encryption + * @return the new request status (for all active admins) - will be one of + * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, or + * {@link #ENCRYPTION_STATUS_ACTIVE}. This is the value of the requests; Use + * {@link #getStorageEncryptionStatus()} to query the actual device state. */ public int setStorageEncryption(ComponentName admin, boolean encrypt) { if (mService != null) { @@ -1182,12 +1172,14 @@ public class DevicePolicyManager { /** * Called by an application that is administering the device to - * determine the encryption status of a specific storage system. + * determine the requested setting for secure storage. * - * @param admin Which {@link DeviceAdminReceiver} this request is associated with. - * @return current status of encryption + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. If null, + * this will return the requested encryption setting as an aggregate of all active + * administrators. + * @return true if the admin(s) are requesting encryption, false if not. */ - public int getStorageEncryption(ComponentName admin) { + public boolean getStorageEncryption(ComponentName admin) { if (mService != null) { try { return mService.getStorageEncryption(admin); @@ -1195,6 +1187,33 @@ public class DevicePolicyManager { Log.w(TAG, "Failed talking with device policy service", e); } } + return false; + } + + /** + * Called by an application that is administering the device to + * determine the current encryption status of the device. + * + * Depending on the returned status code, the caller may proceed in different + * ways. If the result is {@link #ENCRYPTION_STATUS_UNSUPPORTED}, the + * storage system does not support encryption. If the + * result is {@link #ENCRYPTION_STATUS_INACTIVE}, use {@link + * #ACTION_START_ENCRYPTION} to begin the process of encrypting or decrypting the + * storage. If the result is {@link #ENCRYPTION_STATUS_ACTIVATING} or + * {@link #ENCRYPTION_STATUS_ACTIVE}, no further action is required. + * + * @return current status of encryption. The value will be one of + * {@link #ENCRYPTION_STATUS_UNSUPPORTED}, {@link #ENCRYPTION_STATUS_INACTIVE}, + * {@link #ENCRYPTION_STATUS_ACTIVATING}, or{@link #ENCRYPTION_STATUS_ACTIVE}. + */ + public int getStorageEncryptionStatus() { + if (mService != null) { + try { + return mService.getStorageEncryptionStatus(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } return ENCRYPTION_STATUS_UNSUPPORTED; } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index d3b5cf314e8d9..e8caca153467c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -76,7 +76,8 @@ interface IDevicePolicyManager { ComponentName getGlobalProxyAdmin(); int setStorageEncryption(in ComponentName who, boolean encrypt); - int getStorageEncryption(in ComponentName who); + boolean getStorageEncryption(in ComponentName who); + int getStorageEncryptionStatus(); void setActiveAdmin(in ComponentName policyReceiver, boolean refreshing); boolean isAdminActive(in ComponentName policyReceiver); diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index c0ce256b1db61..8a9e351800468 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -145,6 +145,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { int maximumFailedPasswordsForWipe = 0; long passwordExpirationTimeout = 0L; long passwordExpirationDate = 0L; + boolean encryptionRequested = false; // TODO: review implementation decisions with frameworks team boolean specifiesGlobalProxy = false; @@ -242,6 +243,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.attribute(null, "value", Long.toString(passwordExpirationDate)); out.endTag(null, "password-expiration-date"); } + if (encryptionRequested) { + out.startTag(null, "encryption-requested"); + out.attribute(null, "value", Boolean.toString(encryptionRequested)); + out.endTag(null, "encryption-requested"); + } } void readFromXml(XmlPullParser parser) @@ -290,7 +296,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { maximumFailedPasswordsForWipe = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("specifies-global-proxy".equals(tag)) { - specifiesGlobalProxy = Boolean.getBoolean( + specifiesGlobalProxy = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); } else if ("global-proxy-spec".equals(tag)) { globalProxySpec = @@ -304,6 +310,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } else if ("password-expiration-date".equals(tag)) { passwordExpirationDate = Long.parseLong( parser.getAttributeValue(null, "value")); + } else if ("encryption-requested".equals(tag)) { + encryptionRequested = Boolean.parseBoolean( + parser.getAttributeValue(null, "value")); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } @@ -356,6 +365,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { pw.print(prefix); pw.print("globalProxyEclusionList="); pw.println(globalProxyExclusionList); } + pw.print(prefix); pw.print("encryptionRequested="); + pw.println(encryptionRequested); } } @@ -1823,7 +1834,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } /** - * Set the storage encryption request. + * Set the storage encryption request for a single admin. Returns the new total request + * status (for all admins). */ public int setStorageEncryption(ComponentName who, boolean encrypt) { synchronized (this) { @@ -1834,29 +1846,94 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_ENCRYPTED_STORAGE); - // TODO: (1) Record the value for the admin so it's sticky - // TODO: (2) Compute "max" for all admins (if any admin requests encryption, then - // we enable it. - // TODO: (3) Work with filesystem / mount service to start/stop encryption - return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + // Quick exit: If the filesystem does not support encryption, we can exit early. + if (!isEncryptionSupported()) { + return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + } + + // (1) Record the value for the admin so it's sticky + if (ap.encryptionRequested != encrypt) { + ap.encryptionRequested = encrypt; + saveSettingsLocked(); + } + + // (2) Compute "max" for all admins + boolean newRequested = false; + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + newRequested |= mAdminList.get(i).encryptionRequested; + } + + // Notify OS of new request + setEncryptionRequested(newRequested); + + // Return the new global request status + return newRequested + ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE + : DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE; } } /** - * Get the current storage encryption status for a given storage domain. + * Get the current storage encryption request status for a given admin, or aggregate of all + * active admins. */ - public int getStorageEncryption(ComponentName who) { + public boolean getStorageEncryption(ComponentName who) { synchronized (this) { // Check for permissions if a particular caller is specified if (who != null) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_ENCRYPTED_STORAGE); + // When checking for a single caller, status is based on caller's request + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_ENCRYPTED_STORAGE); + return ap.encryptionRequested; } - // TODO: Work with filesystem / mount service to query encryption status - return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + // If no particular caller is specified, return the aggregate set of requests. + // This is short circuited by returning true on the first hit. + final int N = mAdminList.size(); + for (int i = 0; i < N; i++) { + if (mAdminList.get(i).encryptionRequested) { + return true; + } + } + return false; } } + /** + * Get the current encryption status of the device. + */ + public int getStorageEncryptionStatus() { + return getEncryptionStatus(); + } + + /** + * Hook to low-levels: This should report if the filesystem supports encrypted storage. + */ + private boolean isEncryptionSupported() { + // Note, this can be implemented as + // return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + // But is provided as a separate internal method if there's a faster way to do a + // simple check for supported-or-not. + return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + } + + /** + * Hook to low-levels: Reporting the current status of encryption. + * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} or + * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE} or + * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}. + */ + private int getEncryptionStatus() { + return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; + } + + /** + * Hook to low-levels: If needed, record the new admin setting for encryption. + */ + private void setEncryptionRequested(boolean encrypt) { + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)