Add ENCRYPTION_STATUS_ACTIVE_PER_USER to...

getStorageEncryptionStatus()
Use StorageManager APIs to get the encryption
state instead of from the system properties
directly.

Bug 26547262

Change-Id: Ic27baa9489d43a93873f8bb0428084f8886aed67
diff --git a/api/current.txt b/api/current.txt
index e6a5b1a..0e8afbf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5979,6 +5979,7 @@
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+    field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
diff --git a/api/system-current.txt b/api/system-current.txt
index e70b6f6..033ed32 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6130,6 +6130,7 @@
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+    field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
diff --git a/api/test-current.txt b/api/test-current.txt
index 40e1156..4fb2a144 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -5983,6 +5983,7 @@
     field public static final int ENCRYPTION_STATUS_ACTIVATING = 2; // 0x2
     field public static final int ENCRYPTION_STATUS_ACTIVE = 3; // 0x3
     field public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4; // 0x4
+    field public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5; // 0x5
     field public static final int ENCRYPTION_STATUS_INACTIVE = 1; // 0x1
     field public static final int ENCRYPTION_STATUS_UNSUPPORTED = 0; // 0x0
     field public static final java.lang.String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 53a6351..ec1e3e6 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2487,6 +2487,12 @@
     public static final int ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY = 4;
 
     /**
+     * Result code for {@link #getStorageEncryptionStatus}:
+     * indicating that encryption is active and the encryption key is tied to the user.
+     */
+    public static final int ENCRYPTION_STATUS_ACTIVE_PER_USER = 5;
+
+    /**
      * Activity action: begin the process of encrypting data on the device.  This activity should
      * be launched after using {@link #setStorageEncryption} to request encryption be activated.
      * After resuming from this activity, use {@link #getStorageEncryption}
@@ -2627,7 +2633,7 @@
     public int getStorageEncryptionStatus(int userHandle) {
         if (mService != null) {
             try {
-                return mService.getStorageEncryptionStatus(userHandle);
+                return mService.getStorageEncryptionStatus(mContext.getPackageName(), userHandle);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index dc73e26..c38496d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -91,7 +91,7 @@
 
     int setStorageEncryption(in ComponentName who, boolean encrypt);
     boolean getStorageEncryption(in ComponentName who, int userHandle);
-    int getStorageEncryptionStatus(int userHandle);
+    int getStorageEncryptionStatus(in String callerPackage, int userHandle);
 
     boolean requestBugreport(in ComponentName who);
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ea1a569..b48c185 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -33,6 +33,7 @@
 import android.accounts.AccountManager;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -83,6 +84,7 @@
 import android.os.AsyncTask;
 import android.os.Binder;
 import android.os.Build;
+import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.FileUtils;
@@ -1378,6 +1380,22 @@
             return new LockPatternUtils(mContext);
         }
 
+        boolean storageManagerIsFileBasedEncryptionEnabled() {
+            return StorageManager.isFileEncryptedNativeOnly();
+        }
+
+        boolean storageManagerIsNonDefaultBlockEncrypted() {
+            return StorageManager.isNonDefaultBlockEncrypted();
+        }
+
+        boolean storageManagerIsEncrypted() {
+            return StorageManager.isEncrypted();
+        }
+
+        boolean storageManagerIsEncryptable() {
+            return StorageManager.isEncryptable();
+        }
+
         Looper getMyLooper() {
             return Looper.myLooper();
         }
@@ -2866,13 +2884,9 @@
     @Override
     public boolean isSeparateProfileChallengeAllowed(int userHandle) {
         ComponentName profileOwner = getProfileOwner(userHandle);
-        try {
-            // Profile challenge is supported on N or newer release.
-            return profileOwner != null &&
-                    getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
-        } catch (RemoteException e) {
-            return false;
-        }
+        // Profile challenge is supported on N or newer release.
+        return profileOwner != null &&
+                getTargetSdk(profileOwner.getPackageName(), userHandle) > Build.VERSION_CODES.M;
     }
 
     @Override
@@ -4236,15 +4250,12 @@
         int userHandle = UserHandle.getCallingUserId();
         synchronized (this) {
             getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
-            try {
-                if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
-                    if (installerPackage != null &&
-                            !isPackageInstalledForUser(installerPackage, userHandle)) {
-                        throw new IllegalArgumentException("Package " + installerPackage
-                                + " is not installed on the current user");
-                    }
+            if (getTargetSdk(who.getPackageName(), userHandle) >= Build.VERSION_CODES.N) {
+                if (installerPackage != null &&
+                        !isPackageInstalledForUser(installerPackage, userHandle)) {
+                    throw new IllegalArgumentException("Package " + installerPackage
+                            + " is not installed on the current user");
                 }
-            } catch (RemoteException e) {
             }
             DevicePolicyData policy = getUserData(userHandle);
             policy.mDelegatedCertInstallerPackage = installerPackage;
@@ -4835,12 +4846,23 @@
      * Get the current encryption status of the device.
      */
     @Override
-    public int getStorageEncryptionStatus(int userHandle) {
+    public int getStorageEncryptionStatus(@Nullable String callerPackage, int userHandle) {
         if (!mHasFeature) {
             // Ok to return current status.
         }
         enforceFullCrossUsersPermission(userHandle);
-        return getEncryptionStatus();
+
+        // It's not critical here, but let's make sure the package name is correct, in case
+        // we start using it for different purposes.
+        ensureCallerPackage(callerPackage);
+
+        final int rawStatus = getEncryptionStatus();
+        if ((rawStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER)
+                && (callerPackage != null)
+                && (getTargetSdk(callerPackage, userHandle) <= VERSION_CODES.M)) {
+            return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+        }
+        return rawStatus;
     }
 
     /**
@@ -4858,15 +4880,18 @@
      * Hook to low-levels:  Reporting the current status of encryption.
      * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED},
      * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE},
-     * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY}, or
+     * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY},
+     * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE_PER_USER}, or
      * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}.
      */
     private int getEncryptionStatus() {
-        if (StorageManager.isEncrypted()) {
-            return StorageManager.isNonDefaultBlockEncrypted() ?
-                      DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
-                    : DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
-        } else if (StorageManager.isEncryptable()) {
+        if (mInjector.storageManagerIsFileBasedEncryptionEnabled()) {
+            return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
+        } else if (mInjector.storageManagerIsNonDefaultBlockEncrypted()) {
+            return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE;
+        } else if (mInjector.storageManagerIsEncrypted()) {
+            return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY;
+        } else if (mInjector.storageManagerIsEncryptable()) {
             return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE;
         } else {
             return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED;
@@ -4879,7 +4904,6 @@
     private void setEncryptionRequested(boolean encrypt) {
     }
 
-
     /**
      * Set whether the screen capture is disabled for the user managed by the specified admin.
      */
@@ -6037,6 +6061,23 @@
         }
     }
 
+    private void ensureCallerPackage(@Nullable String packageName) {
+        if (packageName == null) {
+            Preconditions.checkState(isCallerWithSystemUid(),
+                    "Only caller can omit package name");
+        } else {
+            final int callingUid = mInjector.binderGetCallingUid();
+            final int userId = mInjector.userHandleGetCallingUserId();
+            try {
+                final ApplicationInfo ai = mIPackageManager.getApplicationInfo(
+                        packageName, 0, userId);
+                Preconditions.checkState(ai.uid == callingUid, "Unmatching package name");
+            } catch (RemoteException e) {
+                // Shouldn't happen
+            }
+        }
+    }
+
     private boolean isCallerWithSystemUid() {
         return UserHandle.isSameApp(mInjector.binderGetCallingUid(), Process.SYSTEM_UID);
     }
@@ -6122,6 +6163,27 @@
                 pw.println(" ");
                 pw.print("    mPasswordOwner="); pw.println(policy.mPasswordOwner);
             }
+            pw.println();
+            pw.println("Encryption Status: " + getEncryptionStatusName(getEncryptionStatus()));
+        }
+    }
+
+    private String getEncryptionStatusName(int encryptionStatus) {
+        switch (encryptionStatus) {
+            case DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE:
+                return "inactive";
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_DEFAULT_KEY:
+                return "block default key";
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE:
+                return "block";
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER:
+                return "per-user";
+            case DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED:
+                return "unsupported";
+            case DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING:
+                return "activating";
+            default:
+                return "unknown";
         }
     }
 
@@ -8207,11 +8269,16 @@
      * Returns the target sdk version number that the given packageName was built for
      * in the given user.
      */
-    private int getTargetSdk(String packageName, int userId) throws RemoteException {
-        final ApplicationInfo ai = mIPackageManager
-                .getApplicationInfo(packageName, 0, userId);
-        final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
-        return targetSdkVersion;
+    private int getTargetSdk(String packageName, int userId) {
+        final ApplicationInfo ai;
+        try {
+            ai = mIPackageManager.getApplicationInfo(packageName, 0, userId);
+            final int targetSdkVersion = ai == null ? 0 : ai.targetSdkVersion;
+            return targetSdkVersion;
+        } catch (RemoteException e) {
+            // Shouldn't happen
+            return 0;
+        }
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index db2a9ad..aaec1e9 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -28,6 +28,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.os.storage.StorageManager;
 import android.view.IWindowManager;
 
 import java.io.File;
@@ -163,6 +164,26 @@
         }
 
         @Override
+        boolean storageManagerIsFileBasedEncryptionEnabled() {
+            return context.storageManager.isFileBasedEncryptionEnabled();
+        }
+
+        @Override
+        boolean storageManagerIsNonDefaultBlockEncrypted() {
+            return context.storageManager.isNonDefaultBlockEncrypted();
+        }
+
+        @Override
+        boolean storageManagerIsEncrypted() {
+            return context.storageManager.isEncrypted();
+        }
+
+        @Override
+        boolean storageManagerIsEncryptable() {
+            return context.storageManager.isEncryptable();
+        }
+
+        @Override
         String getDevicePolicyFilePathForSystemUser() {
             return context.systemUserDataDir.getAbsolutePath() + "/";
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index ef8e420..b05309a 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -39,6 +39,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.UserManagerInternal;
+import android.os.storage.StorageManager;
 import android.test.mock.MockContentResolver;
 import android.test.mock.MockContext;
 import android.view.IWindowManager;
@@ -211,6 +212,24 @@
         }
     }
 
+    public static class StorageManagerForMock {
+        public boolean isFileBasedEncryptionEnabled() {
+            return false;
+        }
+
+        public boolean isNonDefaultBlockEncrypted() {
+            return false;
+        }
+
+        public boolean isEncrypted() {
+            return false;
+        }
+
+        public boolean isEncryptable() {
+            return false;
+        }
+    }
+
     public final Context realTestContext;
 
     /**
@@ -239,6 +258,7 @@
     public final IBackupManager ibackupManager;
     public final IAudioService iaudioService;
     public final LockPatternUtils lockPatternUtils;
+    public final StorageManagerForMock storageManager;
     public final WifiManager wifiManager;
     public final SettingsForMock settings;
     public final MockContentResolver contentResolver;
@@ -272,6 +292,7 @@
         ibackupManager = mock(IBackupManager.class);
         iaudioService = mock(IAudioService.class);
         lockPatternUtils = mock(LockPatternUtils.class);
+        storageManager = mock(StorageManagerForMock.class);
         wifiManager = mock(WifiManager.class);
         settings = mock(SettingsForMock.class);