More file-based encryption work.

Add granular StorageManager APIs for key creation/destruction and
unlocking/locking.  Start passing through an opaque token as part
of the unlock command, but leave it empty for now.  We now have a
separate "prepare" method that sanity checks that user directories
are correctly setup.

Define a handful of system properties used for marking devices that
should be operating in FBE mode, and if they're emulating FBE.  Wire
a command to "sm", but persisting will come later.

Start using new "encryptionAware" flag on apps previously marked with
coreApp flag, which were apps running in the legacy CryptKeeper
model.  Small tweaks to handle non-encryptionAware voice interaction
services.  Switch PackageManager to consult StorageManager about the
unlocked state of a user.

Bug: 22358539
Change-Id: Ic2865f9b81c10ea39369c441422f7427a3c3c3d6
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 52c2f9b..9c880d3 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -22,7 +22,9 @@
 import android.os.Environment;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Printer;
 
@@ -469,6 +471,14 @@
     public static final int PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED = 1 << 5;
 
     /**
+     * When set, assume that all components under the given app are encryption
+     * aware, unless otherwise specified.
+     *
+     * @hide
+     */
+    public static final int PRIVATE_FLAG_ENCRYPTION_AWARE = 1 << 6;
+
+    /**
      * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
      * {@hide}
      */
@@ -963,7 +973,8 @@
                 .getDataUserCredentialEncryptedPackageDirectory(volumeUuid, userId, packageName)
                 .getAbsolutePath();
 
-        if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0) {
+        if ((privateFlags & PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED) != 0
+                && SystemProperties.getBoolean(StorageManager.PROP_HAS_FBE, false)) {
             dataDir = deviceEncryptedDataDir;
         } else {
             dataDir = credentialEncryptedDataDir;
@@ -1030,6 +1041,11 @@
                 && (flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
     }
 
+    /** @hide */
+    public boolean isEncryptionAware() {
+        return (privateFlags & ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE) != 0;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f176d89..838da37 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2636,6 +2636,10 @@
                 && (flags & PARSE_IS_SYSTEM) != 0) {
             ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_FORCE_DEVICE_ENCRYPTED;
         }
+        if (sa.getBoolean(R.styleable.AndroidManifestApplication_encryptionAware, false)
+                && (flags & PARSE_IS_SYSTEM) != 0) {
+            ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ENCRYPTION_AWARE;
+        }
 
         String str;
         str = sa.getNonConfigurationString(
@@ -3236,7 +3240,8 @@
                     sa.getInt(R.styleable.AndroidManifestActivity_lockTaskMode, 0);
 
             a.info.encryptionAware = sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_encryptionAware, false);
+                    R.styleable.AndroidManifestActivity_encryptionAware,
+                    owner.applicationInfo.isEncryptionAware());
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -3253,7 +3258,8 @@
             }
 
             a.info.encryptionAware = sa.getBoolean(
-                    R.styleable.AndroidManifestActivity_encryptionAware, false);
+                    R.styleable.AndroidManifestActivity_encryptionAware,
+                    owner.applicationInfo.isEncryptionAware());
         }
 
         sa.recycle();
@@ -3655,7 +3661,8 @@
         }
 
         p.info.encryptionAware = sa.getBoolean(
-                R.styleable.AndroidManifestProvider_encryptionAware, false);
+                R.styleable.AndroidManifestProvider_encryptionAware,
+                owner.applicationInfo.isEncryptionAware());
 
         sa.recycle();
 
@@ -3938,7 +3945,8 @@
         }
 
         s.info.encryptionAware = sa.getBoolean(
-                R.styleable.AndroidManifestService_encryptionAware, false);
+                R.styleable.AndroidManifestService_encryptionAware,
+                owner.applicationInfo.isEncryptionAware());
 
         sa.recycle();
 
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 5d45ade..d19c7c9 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1198,14 +1198,14 @@
             }
 
             @Override
-            public void createNewUserDir(int userHandle, String path) throws RemoteException {
+            public void createUserKey(int userId, int serialNumber) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
-                    _data.writeInt(userHandle);
-                    _data.writeString(path);
-                    mRemote.transact(Stub.TRANSACTION_createNewUserDir, _data, _reply, 0);
+                    _data.writeInt(userId);
+                    _data.writeInt(serialNumber);
+                    mRemote.transact(Stub.TRANSACTION_createUserKey, _data, _reply, 0);
                     _reply.readException();
                 } finally {
                     _reply.recycle();
@@ -1214,13 +1214,81 @@
             }
 
             @Override
-            public void deleteUserKey(int userHandle) throws RemoteException {
+            public void destroyUserKey(int userId) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
-                    _data.writeInt(userHandle);
-                    mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+                    _data.writeInt(userId);
+                    mRemote.transact(Stub.TRANSACTION_destroyUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
+                    _data.writeInt(serialNumber);
+                    _data.writeByteArray(token);
+                    mRemote.transact(Stub.TRANSACTION_unlockUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public void lockUserKey(int userId) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
+                    mRemote.transact(Stub.TRANSACTION_lockUserKey, _data, _reply, 0);
+                    _reply.readException();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+            }
+
+            @Override
+            public boolean isUserKeyUnlocked(int userId) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                boolean _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(userId);
+                    mRemote.transact(Stub.TRANSACTION_isUserKeyUnlocked, _data, _reply, 0);
+                    _reply.readException();
+                    _result = 0 != _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
+            @Override
+            public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+                    throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(volumeUuid);
+                    _data.writeInt(userId);
+                    _data.writeInt(serialNumber);
+                    mRemote.transact(Stub.TRANSACTION_prepareUserStorage, _data, _reply, 0);
                     _reply.readException();
                 } finally {
                     _reply.recycle();
@@ -1359,13 +1427,17 @@
         static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
         static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
 
-        static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62;
+        static final int TRANSACTION_createUserKey = IBinder.FIRST_CALL_TRANSACTION + 61;
+        static final int TRANSACTION_destroyUserKey = IBinder.FIRST_CALL_TRANSACTION + 62;
 
-        static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+        static final int TRANSACTION_unlockUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+        static final int TRANSACTION_lockUserKey = IBinder.FIRST_CALL_TRANSACTION + 64;
+        static final int TRANSACTION_isUserKeyUnlocked = IBinder.FIRST_CALL_TRANSACTION + 65;
 
-        static final int TRANSACTION_isPerUserEncryptionEnabled = IBinder.FIRST_CALL_TRANSACTION + 64;
+        static final int TRANSACTION_prepareUserStorage = IBinder.FIRST_CALL_TRANSACTION + 66;
 
-        static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 65;
+        static final int TRANSACTION_isPerUserEncryptionEnabled = IBinder.FIRST_CALL_TRANSACTION + 67;
+        static final int TRANSACTION_isConvertibleToFBE = IBinder.FIRST_CALL_TRANSACTION + 68;
 
         /**
          * Cast an IBinder object into an IMountService interface, generating a
@@ -1929,18 +2001,51 @@
                     reply.writeNoException();
                     return true;
                 }
-                case TRANSACTION_createNewUserDir: {
+                case TRANSACTION_createUserKey: {
                     data.enforceInterface(DESCRIPTOR);
-                    int userHandle = data.readInt();
-                    String path = data.readString();
-                    createNewUserDir(userHandle, path);
+                    int userId = data.readInt();
+                    int serialNumber = data.readInt();
+                    createUserKey(userId, serialNumber);
                     reply.writeNoException();
                     return true;
                 }
-                case TRANSACTION_deleteUserKey: {
+                case TRANSACTION_destroyUserKey: {
                     data.enforceInterface(DESCRIPTOR);
-                    int userHandle = data.readInt();
-                    deleteUserKey(userHandle);
+                    int userId = data.readInt();
+                    destroyUserKey(userId);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_unlockUserKey: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userId = data.readInt();
+                    int serialNumber = data.readInt();
+                    byte[] token = data.createByteArray();
+                    unlockUserKey(userId, serialNumber, token);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_lockUserKey: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userId = data.readInt();
+                    lockUserKey(userId);
+                    reply.writeNoException();
+                    return true;
+                }
+                case TRANSACTION_isUserKeyUnlocked: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int userId = data.readInt();
+                    boolean result = isUserKeyUnlocked(userId);
+                    reply.writeNoException();
+                    reply.writeInt(result ? 1 : 0);
+                    return true;
+                }
+                case TRANSACTION_prepareUserStorage: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String volumeUuid = data.readString();
+                    int userId = data.readInt();
+                    int serialNumber = data.readInt();
+                    prepareUserStorage(volumeUuid, userId, serialNumber);
                     reply.writeNoException();
                     return true;
                 }
@@ -1948,7 +2053,7 @@
                     data.enforceInterface(DESCRIPTOR);
                     boolean result = isPerUserEncryptionEnabled();
                     reply.writeNoException();
-                    reply.writeInt((result ? 1 : 0));
+                    reply.writeInt(result ? 1 : 0);
                     return true;
                 }
             }
@@ -2263,24 +2368,15 @@
     public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
             throws RemoteException;
 
-    /**
-     * Creates the user data directory, possibly encrypted
-     * @param userHandle Handle of the user whose directory we are creating
-     * @param path Path at which to create the directory.
-     */
-    public void createNewUserDir(int userHandle, String path)
-        throws RemoteException;
+    public void createUserKey(int userId, int serialNumber) throws RemoteException;
+    public void destroyUserKey(int userId) throws RemoteException;
 
-    /**
-     * Securely delete the user's encryption key
-     * @param userHandle Handle of the user whose key we are deleting
-     */
-    public void deleteUserKey(int userHandle)
-        throws RemoteException;
+    public void unlockUserKey(int userId, int serialNumber, byte[] token) throws RemoteException;
+    public void lockUserKey(int userId) throws RemoteException;
+    public boolean isUserKeyUnlocked(int userId) throws RemoteException;
 
-    /**
-     * Returns whether the current encryption type is per user.
-     */
-    public boolean isPerUserEncryptionEnabled()
-        throws RemoteException;
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber)
+            throws RemoteException;
+
+    public boolean isPerUserEncryptionEnabled() throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 2ca0b20..27df46d 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -76,7 +76,11 @@
     /** {@hide} */
     public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable";
     /** {@hide} */
+    public static final String PROP_HAS_FBE = "vold.has_fbe";
+    /** {@hide} */
     public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable";
+    /** {@hide} */
+    public static final String PROP_EMULATE_FBE = "vold.emulate_fbe";
 
     /** {@hide} */
     public static final String UUID_PRIVATE_INTERNAL = null;
@@ -85,6 +89,8 @@
 
     /** {@hide} */
     public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
+    /** {@hide} */
+    public static final int DEBUG_EMULATE_FBE = 1 << 1;
 
     /** {@hide} */
     public static final int FLAG_FOR_WRITE = 1 << 0;
@@ -960,18 +966,54 @@
     }
 
     /** {@hide} */
-    public void createNewUserDir(int userHandle, File path) {
+    public void createUserKey(int userId, int serialNumber) {
         try {
-            mMountService.createNewUserDir(userHandle, path.getAbsolutePath());
+            mMountService.createUserKey(userId, serialNumber);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
     }
 
     /** {@hide} */
-    public void deleteUserKey(int userHandle) {
+    public void destroyUserKey(int userId) {
         try {
-            mMountService.deleteUserKey(userHandle);
+            mMountService.destroyUserKey(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public void unlockUserKey(int userId, int serialNumber, byte[] token) {
+        try {
+            mMountService.unlockUserKey(userId, serialNumber, token);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public void lockUserKey(int userId) {
+        try {
+            mMountService.lockUserKey(userId);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public void prepareUserStorage(String volumeUuid, int userId, int serialNumber) {
+        try {
+            mMountService.prepareUserStorage(volumeUuid, userId, serialNumber);
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    /** {@hide} */
+    public boolean isUserKeyUnlocked(int userId) {
+        try {
+            return mMountService.isUserKeyUnlocked(userId);
         } catch (RemoteException e) {
             throw e.rethrowAsRuntimeException();
         }
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index f190d8c..e8970bc 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -144,6 +144,13 @@
     }
 
     /**
+     * Checks if given array is null or has zero elements.
+     */
+    public static boolean isEmpty(byte[] array) {
+        return array == null || array.length == 0;
+    }
+
+    /**
      * Checks that value is present as at least one of the elements of the array.
      * @param array the array to check in
      * @param value the value to check for
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 561bcbc..6bdf71b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1683,6 +1683,10 @@
     <permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
         android:protectionLevel="system|signature" />
 
+    <!-- @hide -->
+    <permission android:name="android.permission.STORAGE_INTERNAL"
+        android:protectionLevel="signature" />
+
     <!-- Allows access to ASEC non-destructive API calls
          @hide  -->
     <permission android:name="android.permission.ASEC_ACCESS"
@@ -2702,7 +2706,9 @@
                  android:killAfterRestore="false"
                  android:icon="@drawable/ic_launcher_android"
                  android:supportsRtl="true"
-                 android:theme="@style/Theme.Material.DayNight.DarkActionBar">
+                 android:theme="@style/Theme.Material.DayNight.DarkActionBar"
+                 android:forceDeviceEncrypted="true"
+                 android:encryptionAware="true">
         <activity android:name="com.android.internal.app.ChooserActivity"
                 android:theme="@style/Theme.DeviceDefault.Resolver"
                 android:finishOnCloseSystemDialogs="true"
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 184f2ab..e376903 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1283,6 +1283,7 @@
         <attr name="multiArch" />
         <attr name="extractNativeLibs" />
         <attr name="forceDeviceEncrypted" format="boolean" />
+        <attr name="encryptionAware" />
     </declare-styleable>
     <!-- The <code>permission</code> tag declares a security permission that can be
          used to control access from other packages to specific components or
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 037f1c4..43a6acd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2679,6 +2679,8 @@
     <public type="attr" name="contextPopupMenuStyle" />
     <public type="attr" name="textAppearancePopupMenuHeader" />
     <public type="attr" name="windowBackgroundFallback" />
+    <public type="attr" name="forceDeviceEncrypted" />
+    <public type="attr" name="encryptionAware" />
 
     <public type="style" name="Theme.Material.DayNight" />
     <public type="style" name="Theme.Material.DayNight.DarkActionBar" />