Merge "Support default, pattern, pin and password encryption types"
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 51ba2f6..b97734e 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -642,12 +642,13 @@
                 return _result;
             }
 
-            public int changeEncryptionPassword(String password) throws RemoteException {
+            public int changeEncryptionPassword(int type, String password) throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
                 int _result;
                 try {
                     _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeInt(type);
                     _data.writeString(password);
                     mRemote.transact(Stub.TRANSACTION_changeEncryptionPassword, _data, _reply, 0);
                     _reply.readException();
@@ -677,6 +678,22 @@
                 return _result;
             }
 
+            public int getPasswordType() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_getPasswordType, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
+
             public StorageVolume[] getVolumeList() throws RemoteException {
                 Parcel _data = Parcel.obtain();
                 Parcel _reply = Parcel.obtain();
@@ -829,6 +846,8 @@
 
         static final int TRANSACTION_mkdirs = IBinder.FIRST_CALL_TRANSACTION + 34;
 
+        static final int TRANSACTION_getPasswordType = IBinder.FIRST_CALL_TRANSACTION + 36;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1130,8 +1149,9 @@
                 }
                 case TRANSACTION_changeEncryptionPassword: {
                     data.enforceInterface(DESCRIPTOR);
+                    int type = data.readInt();
                     String password = data.readString();
-                    int result = changeEncryptionPassword(password);
+                    int result = changeEncryptionPassword(type, password);
                     reply.writeNoException();
                     reply.writeInt(result);
                     return true;
@@ -1181,6 +1201,13 @@
                     reply.writeInt(result);
                     return true;
                 }
+                case TRANSACTION_getPasswordType: {
+                    data.enforceInterface(DESCRIPTOR);
+                    int result = getPasswordType();
+                    reply.writeNoException();
+                    reply.writeInt(result);
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1375,7 +1402,8 @@
     /**
      * Changes the encryption password.
      */
-    public int changeEncryptionPassword(String password) throws RemoteException;
+    public int changeEncryptionPassword(int type, String password)
+        throws RemoteException;
 
     /**
      * Verify the encryption password against the stored volume.  This method
@@ -1412,4 +1440,10 @@
      * external storage data or OBB directory belonging to calling app.
      */
     public int mkdirs(String callingPkg, String path) throws RemoteException;
+
+    /**
+     * Determines the type of the encryption password
+     * @return PasswordType
+     */
+    public int getPasswordType() throws RemoteException;
 }
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f5e728d..68b91cb 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -645,4 +645,14 @@
         return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES,
                 DEFAULT_FULL_THRESHOLD_BYTES);
     }
+
+    /// Consts to match the password types in cryptfs.h
+    /** @hide */
+    public static final int CRYPT_TYPE_PASSWORD = 0;
+    /** @hide */
+    public static final int CRYPT_TYPE_DEFAULT = 1;
+    /** @hide */
+    public static final int CRYPT_TYPE_PATTERN = 2;
+    /** @hide */
+    public static final int CRYPT_TYPE_PIN = 3;
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3419f15..f5c18f5 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -30,10 +30,12 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.storage.IMountService;
+import android.os.storage.StorageManager;
 import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Slog;
 import android.view.IWindowManager;
 import android.view.View;
 import android.widget.Button;
@@ -498,6 +500,13 @@
             getLockSettings().setLockPattern(patternToString(pattern), getCurrentOrCallingUserId());
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (pattern != null) {
+
+                int userHandle = getCurrentOrCallingUserId();
+                if (userHandle == UserHandle.USER_OWNER) {
+                    String stringPattern = patternToString(pattern);
+                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern);
+                }
+
                 setBoolean(PATTERN_EVER_CHOSEN_KEY, true);
                 if (!isFallback) {
                     deleteGallery();
@@ -565,7 +574,7 @@
     }
 
     /** Update the encryption password if it is enabled **/
-    private void updateEncryptionPassword(String password) {
+    private void updateEncryptionPassword(int type, String password) {
         DevicePolicyManager dpm = getDevicePolicyManager();
         if (dpm.getStorageEncryptionStatus(getCurrentOrCallingUserId())
                 != DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE) {
@@ -580,7 +589,7 @@
 
         IMountService mountService = IMountService.Stub.asInterface(service);
         try {
-            mountService.changeEncryptionPassword(password);
+            mountService.changeEncryptionPassword(type, password);
         } catch (RemoteException e) {
             Log.e(TAG, "Error changing encryption password", e);
         }
@@ -623,12 +632,15 @@
             getLockSettings().setLockPassword(password, userHandle);
             DevicePolicyManager dpm = getDevicePolicyManager();
             if (password != null) {
+                int computedQuality = computePasswordQuality(password);
+
                 if (userHandle == UserHandle.USER_OWNER) {
                     // Update the encryption password.
-                    updateEncryptionPassword(password);
+                    int type = computedQuality == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+                        ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+                    updateEncryptionPassword(type, password);
                 }
 
-                int computedQuality = computePasswordQuality(password);
                 if (!isFallback) {
                     deleteGallery();
                     setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle);
@@ -675,8 +687,7 @@
                             0, 0, 0, 0, 0, 0, 0, userHandle);
                 }
                 // Add the password to the password history. We assume all
-                // password
-                // hashes have the same length for simplicity of implementation.
+                // password hashes have the same length for simplicity of implementation.
                 String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle);
                 if (passwordHistory == null) {
                     passwordHistory = new String();
@@ -695,6 +706,11 @@
                 }
                 setString(PASSWORD_HISTORY_KEY, passwordHistory, userHandle);
             } else {
+                if (userHandle == UserHandle.USER_OWNER) {
+                    // Update the encryption password.
+                    updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, password);
+                }
+
                 dpm.setActivePasswordState(
                         DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0,
                         userHandle);
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index e6e4bca..0f2e56c 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -73,13 +73,16 @@
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import org.apache.commons.codec.binary.Hex;
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
@@ -189,6 +192,12 @@
         public static final int FstrimCompleted                = 700;
     }
 
+    /** List of crypto types.
+      * These must match CRYPT_TYPE_XXX in cryptfs.h AND their
+      * corresponding commands in CommandListener.cpp */
+    public static final String[] CRYPTO_TYPES
+        = { "password", "default", "pattern", "pin" };
+
     private Context mContext;
     private NativeDaemonConnector mConnector;
 
@@ -2036,6 +2045,14 @@
         }
     }
 
+    private String toHex(String password) {
+        if (password == null) {
+            return null;
+        }
+        byte[] bytes = password.getBytes(StandardCharsets.UTF_8);
+        return new String(Hex.encodeHex(bytes));
+    }
+
     @Override
     public int decryptStorage(String password) {
         if (TextUtils.isEmpty(password)) {
@@ -2053,7 +2070,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(password));
+            event = mConnector.execute("cryptfs", "checkpw", new SensitiveArg(toHex(password)));
 
             final int code = Integer.parseInt(event.getMessage());
             if (code == 0) {
@@ -2092,7 +2109,8 @@
         }
 
         try {
-            mConnector.execute("cryptfs", "enablecrypto", "inplace", new SensitiveArg(password));
+            mConnector.execute("cryptfs", "enablecrypto", "inplace",
+                               new SensitiveArg(toHex(password)));
         } catch (NativeDaemonConnectorException e) {
             // Encryption failed
             return e.getCode();
@@ -2101,11 +2119,11 @@
         return 0;
     }
 
-    public int changeEncryptionPassword(String password) {
-        if (TextUtils.isEmpty(password)) {
-            throw new IllegalArgumentException("password cannot be empty");
-        }
-
+    /** Set the password for encrypting the master key.
+     *  @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
+     *  @param password The password to set.
+     */
+    public int changeEncryptionPassword(int type, String password) {
         mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER,
             "no permission to access the crypt keeper");
 
@@ -2117,7 +2135,8 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "changepw", new SensitiveArg(password));
+            event = mConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
+                                       new SensitiveArg(toHex(password)));
             return Integer.parseInt(event.getMessage());
         } catch (NativeDaemonConnectorException e) {
             // Encryption failed
@@ -2150,7 +2169,7 @@
 
         final NativeDaemonEvent event;
         try {
-            event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(password));
+            event = mConnector.execute("cryptfs", "verifypw", new SensitiveArg(toHex(password)));
             Slog.i(TAG, "cryptfs verifypw => " + event.getMessage());
             return Integer.parseInt(event.getMessage());
         } catch (NativeDaemonConnectorException e) {
@@ -2159,6 +2178,29 @@
         }
     }
 
+    /**
+     * Get the type of encryption used to encrypt the master key.
+     * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager.
+     */
+    @Override
+    public int getPasswordType() throws RemoteException {
+
+        waitForReady();
+
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("cryptfs", "getpwtype");
+            for (int i = 0; i < CRYPTO_TYPES.length; ++i) {
+                if (CRYPTO_TYPES[i].equals(event.getMessage()))
+                    return i;
+            }
+
+            throw new IllegalStateException("unexpected return from cryptfs");
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
     @Override
     public int mkdirs(String callingPkg, String appPath) {
         final int userId = UserHandle.getUserId(Binder.getCallingUid());