Merge "Start passing volume UUID to installd."
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java
index a4cdf19..671bf24 100644
--- a/core/java/com/android/internal/os/InstallerConnection.java
+++ b/core/java/com/android/internal/os/InstallerConnection.java
@@ -90,12 +90,15 @@
         }
     }
 
-    public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        return dexopt(apkPath, uid, isPublic, "*", instructionSet, false, false, null);
+    public int dexopt(String apkPath, int uid, boolean isPublic,
+            String instructionSet, int dexoptNeeded) {
+        return dexopt(apkPath, uid, isPublic, "*", instructionSet, dexoptNeeded,
+                false, false, null);
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, boolean vmSafeMode, boolean debuggable, String outputPath) {
+            String instructionSet, int dexoptNeeded, boolean vmSafeMode,
+            boolean debuggable, String outputPath) {
         StringBuilder builder = new StringBuilder("dexopt");
         builder.append(' ');
         builder.append(apkPath);
@@ -106,6 +109,8 @@
         builder.append(pkgName);
         builder.append(' ');
         builder.append(instructionSet);
+        builder.append(' ');
+        builder.append(dexoptNeeded);
         builder.append(vmSafeMode ? " 1" : " 0");
         builder.append(debuggable ? " 1" : " 0");
         builder.append(' ');
@@ -113,25 +118,6 @@
         return execute(builder.toString());
     }
 
-    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        return patchoat(apkPath, uid, isPublic, "*", instructionSet);
-    }
-
-    public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet) {
-        StringBuilder builder = new StringBuilder("patchoat");
-        builder.append(' ');
-        builder.append(apkPath);
-        builder.append(' ');
-        builder.append(uid);
-        builder.append(isPublic ? " 1" : " 0");
-        builder.append(' ');
-        builder.append(pkgName);
-        builder.append(' ');
-        builder.append(instructionSet);
-        return execute(builder.toString());
-    }
-
     private boolean connect() {
         if (mSocket != null) {
             return true;
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 98638ed..50ddbd1 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -465,12 +465,11 @@
 
         try {
             for (String classPathElement : classPathElements) {
-                final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
-                        false /* defer */);
-                if (dexopt == DexFile.DEXOPT_NEEDED) {
-                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
-                } else if (dexopt == DexFile.PATCHOAT_NEEDED) {
-                    installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
+                final int dexoptNeeded = DexFile.getDexOptNeeded(
+                        classPathElement, "*", instructionSet, false /* defer */);
+                if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                    installer.dexopt(classPathElement, Process.SYSTEM_UID, false,
+                            instructionSet, dexoptNeeded);
                 }
             }
         } catch (IOException ioe) {
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 8673219..0822afd 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -112,7 +112,7 @@
  *
  * The PNG chunk type is "npTc".
  */
-struct Res_png_9patch
+struct alignas(uintptr_t) Res_png_9patch
 {
     Res_png_9patch() : wasDeserialized(false), xDivsOffset(0),
                        yDivsOffset(0), colorsOffset(0) { }
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index f72c7acd..55a8b4f 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -512,12 +512,23 @@
             }
         }
 
-        int purposes = params.getPurposes();
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = params.getPurposes();
+        @KeyStoreKeyConstraints.BlockModeEnum int blockModes = params.getBlockModes();
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+                && (params.isRandomizedEncryptionRequired())) {
+            @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes =
+                    blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES;
+            if (incompatibleBlockModes != 0) {
+                throw new KeyStoreException("Randomized encryption (IND-CPA) required but may be"
+                        + " violated by block mode(s): "
+                        + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes)
+                        + ". See KeyStoreParameter documentation.");
+            }
+        }
         for (int keymasterPurpose : KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
             args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
         }
-        for (int keymasterBlockMode :
-            KeyStoreKeyConstraints.BlockMode.allToKeymaster(params.getBlockModes())) {
+        for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
             args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
         }
         for (int keymasterPadding :
@@ -549,8 +560,8 @@
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keyMaterial.length * 8);
 
         if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
-                || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
-            // Permit caller-specified IV. This is needed for the Cipher abstraction.
+                && (!params.isRandomizedEncryptionRequired())) {
+            // Permit caller-provided IV when encrypting with this key
             args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
         }
 
diff --git a/keystore/java/android/security/AndroidKeyStoreProvider.java b/keystore/java/android/security/AndroidKeyStoreProvider.java
index 635b2fa..43f3b30 100644
--- a/keystore/java/android/security/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/AndroidKeyStoreProvider.java
@@ -64,6 +64,7 @@
         putSecretKeyFactoryImpl("HmacSHA512");
 
         // javax.crypto.Mac
+        putMacImpl("HmacSHA1", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA1");
         putMacImpl("HmacSHA224", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA224");
         putMacImpl("HmacSHA256", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA256");
         putMacImpl("HmacSHA384", PACKAGE_NAME + ".KeyStoreHmacSpi$HmacSHA384");
diff --git a/keystore/java/android/security/KeyGeneratorSpec.java b/keystore/java/android/security/KeyGeneratorSpec.java
index 3ada7f6..4dcabd0 100644
--- a/keystore/java/android/security/KeyGeneratorSpec.java
+++ b/keystore/java/android/security/KeyGeneratorSpec.java
@@ -22,6 +22,7 @@
 import java.security.spec.AlgorithmParameterSpec;
 import java.util.Date;
 
+import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
 
@@ -51,6 +52,7 @@
     private final @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
     private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
     private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+    private final boolean mRandomizedEncryptionRequired;
     private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
     private final int mUserAuthenticationValidityDurationSeconds;
 
@@ -65,6 +67,7 @@
             @KeyStoreKeyConstraints.PurposeEnum int purposes,
             @KeyStoreKeyConstraints.PaddingEnum int paddings,
             @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            boolean randomizedEncryptionRequired,
             @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
             int userAuthenticationValidityDurationSeconds) {
         if (context == null) {
@@ -87,6 +90,7 @@
         mPurposes = purposes;
         mPaddings = paddings;
         mBlockModes = blockModes;
+        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
     }
@@ -169,6 +173,19 @@
     }
 
     /**
+     * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
+     * different ciphertexts for the same plaintext every time. The formal cryptographic property
+     * being required is <em>indistinguishability under chosen-plaintext attack ({@code
+     * IND-CPA})</em>. This property is important because it mitigates several classes of
+     * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
+     * given plaintext always produces the same ciphertext, an attacker may see the repeated
+     * ciphertexts and be able to deduce something about the plaintext.
+     */
+    public boolean isRandomizedEncryptionRequired() {
+        return mRandomizedEncryptionRequired;
+    }
+
+    /**
      * Gets the set of user authenticators which protect access to this key. The key can only be
      * used iff the user has authenticated to at least one of these user authenticators.
      *
@@ -207,6 +224,7 @@
         private @KeyStoreKeyConstraints.PurposeEnum int mPurposes;
         private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
         private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+        private boolean mRandomizedEncryptionRequired = true;
         private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
         private int mUserAuthenticationValidityDurationSeconds = -1;
 
@@ -264,7 +282,7 @@
         /**
          * Sets the time instant before which the key is not yet valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityEnd(Date)
          */
@@ -276,7 +294,7 @@
         /**
          * Sets the time instant after which the key is no longer valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
@@ -291,7 +309,7 @@
         /**
          * Sets the time instant after which the key is no longer valid for encryption and signing.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          */
@@ -304,7 +322,7 @@
          * Sets the time instant after which the key is no longer valid for decryption and
          * verification.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          */
@@ -346,6 +364,43 @@
         }
 
         /**
+         * Sets whether encryption using this key must be sufficiently randomized to produce
+         * different ciphertexts for the same plaintext every time. The formal cryptographic
+         * property being required is <em>indistinguishability under chosen-plaintext attack
+         * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
+         * of weaknesses due to which ciphertext may leak information about plaintext. For example,
+         * if a given plaintext always produces the same ciphertext, an attacker may see the
+         * repeated ciphertexts and be able to deduce something about the plaintext.
+         *
+         * <p>By default, {@code IND-CPA} is required.
+         *
+         * <p>When {@code IND-CPA} is required:
+         * <ul>
+         * <li>block modes which do not offer {@code IND-CPA}, such as {@code ECB}, are prohibited;
+         * </li>
+         * <li>in block modes which use an IV, such as {@code CBC}, {@code CTR}, and {@code GCM},
+         * caller-provided IVs are rejected when encrypting, to ensure that only random IVs are
+         * used.</li>
+         *
+         * <p>Before disabling this requirement, consider the following approaches instead:
+         * <ul>
+         * <li>If you are generating a random IV for encryption and then initializing a {@code}
+         * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV
+         * instead. This will occur if the {@code Cipher} is initialized for encryption without an
+         * IV. The IV can then be queried via {@link Cipher#getIV()}.</li>
+         * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully
+         * random, such as the name of the file being encrypted, or transaction ID, or password,
+         * or a device identifier), consider changing your design to use a random IV which will then
+         * be provided in addition to the ciphertext to the entities which need to decrypt the
+         * ciphertext.</li>
+         * </ul>
+         */
+        public Builder setRandomizedEncryptionRequired(boolean required) {
+            mRandomizedEncryptionRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the user authenticators which protect access to this key. The key can only be used
          * iff the user has authenticated to at least one of these user authenticators.
          *
@@ -394,6 +449,7 @@
                     mPurposes,
                     mPaddings,
                     mBlockModes,
+                    mRandomizedEncryptionRequired,
                     mUserAuthenticators,
                     mUserAuthenticationValidityDurationSeconds);
         }
diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java
index edaa9a5..db310ea 100644
--- a/keystore/java/android/security/KeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/KeyPairGeneratorSpec.java
@@ -86,6 +86,8 @@
 
     private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
 
+    private final boolean mRandomizedEncryptionRequired;
+
     private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
 
     private final int mUserAuthenticationValidityDurationSeconds;
@@ -132,6 +134,7 @@
             @KeyStoreKeyConstraints.DigestEnum int digests,
             @KeyStoreKeyConstraints.PaddingEnum int paddings,
             @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            boolean randomizedEncryptionRequired,
             @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
             int userAuthenticationValidityDurationSeconds) {
         if (context == null) {
@@ -171,6 +174,7 @@
         mDigests = digests;
         mPaddings = paddings;
         mBlockModes = blockModes;
+        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
     }
@@ -182,8 +186,26 @@
     public KeyPairGeneratorSpec(Context context, String keyStoreAlias, String keyType, int keySize,
             AlgorithmParameterSpec spec, X500Principal subjectDN, BigInteger serialNumber,
             Date startDate, Date endDate, int flags) {
-        this(context, keyStoreAlias, keyType, keySize, spec, subjectDN, serialNumber, startDate,
-                endDate, flags, startDate, endDate, endDate, 0, 0, 0, 0, 0, -1);
+        this(context,
+                keyStoreAlias,
+                keyType,
+                keySize,
+                spec,
+                subjectDN,
+                serialNumber,
+                startDate,
+                endDate,
+                flags,
+                startDate,
+                endDate,
+                endDate,
+                0,
+                0,
+                0,
+                0,
+                true,
+                0,
+                -1);
     }
 
     /**
@@ -343,6 +365,21 @@
     }
 
     /**
+     * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
+     * different ciphertexts for the same plaintext every time. The formal cryptographic property
+     * being required is <em>indistinguishability under chosen-plaintext attack ({@code
+     * IND-CPA})</em>. This property is important because it mitigates several classes of
+     * weaknesses due to which ciphertext may leak information about plaintext.  For example, if a
+     * given plaintext always produces the same ciphertext, an attacker may see the repeated
+     * ciphertexts and be able to deduce something about the plaintext.
+     *
+     * @hide
+     */
+    public boolean isRandomizedEncryptionRequired() {
+        return mRandomizedEncryptionRequired;
+    }
+
+    /**
      * Gets the set of user authenticators which protect access to the private key. The key can only
      * be used iff the user has authenticated to at least one of these user authenticators.
      *
@@ -429,6 +466,8 @@
 
         private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
 
+        private boolean mRandomizedEncryptionRequired = true;
+
         private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
 
         private int mUserAuthenticationValidityDurationSeconds = -1;
@@ -561,7 +600,7 @@
         /**
          * Sets the time instant before which the key is not yet valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityEnd(Date)
          *
@@ -575,7 +614,7 @@
         /**
          * Sets the time instant after which the key is no longer valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
@@ -592,7 +631,7 @@
         /**
          * Sets the time instant after which the key is no longer valid for encryption and signing.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          *
@@ -607,7 +646,7 @@
          * Sets the time instant after which the key is no longer valid for decryption and
          * verification.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          *
@@ -670,6 +709,33 @@
         }
 
         /**
+         * Sets whether encryption using this key must be sufficiently randomized to produce
+         * different ciphertexts for the same plaintext every time. The formal cryptographic
+         * property being required is <em>indistinguishability under chosen-plaintext attack
+         * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
+         * of weaknesses due to which ciphertext may leak information about plaintext. For example,
+         * if a given plaintext always produces the same ciphertext, an attacker may see the
+         * repeated ciphertexts and be able to deduce something about the plaintext.
+         *
+         * <p>By default, {@code IND-CPA} is required.
+         *
+         * <p>When {@code IND-CPA} is required, encryption/decryption transformations which do not
+         * offer {@code IND-CPA}, such as RSA without padding, are prohibited.
+         *
+         * <p>Before disabling this requirement, consider the following approaches instead:
+         * <ul>
+         * <li>If you are using RSA encryption without padding, consider switching to padding
+         * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
+         * </ul>
+         *
+         * @hide
+         */
+        public Builder setRandomizedEncryptionRequired(boolean required) {
+            mRandomizedEncryptionRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the user authenticators which protect access to this key. The key can only be used
          * iff the user has authenticated to at least one of these user authenticators.
          *
@@ -736,6 +802,7 @@
                     mDigests,
                     mPaddings,
                     mBlockModes,
+                    mRandomizedEncryptionRequired,
                     mUserAuthenticators,
                     mUserAuthenticationValidityDurationSeconds);
         }
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index 9321302..cde27f9 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -123,7 +123,7 @@
     }
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Algorithm.AES, Algorithm.HMAC})
+    @IntDef({Algorithm.AES, Algorithm.HMAC, Algorithm.RSA, Algorithm.EC})
     public @interface AlgorithmEnum {}
 
     /**
@@ -135,12 +135,22 @@
         /**
          * Key algorithm: AES.
          */
-        public static final int AES = 0;
+        public static final int AES = 1 << 0;
 
         /**
          * Key algorithm: HMAC.
          */
-        public static final int HMAC = 1;
+        public static final int HMAC = 1 << 1;
+
+        /**
+         * Key algorithm: RSA.
+         */
+        public static final int RSA = 1 << 2;
+
+        /**
+         * Key algorithm: EC.
+         */
+        public static final int EC = 1 << 3;
 
         /**
          * @hide
@@ -151,6 +161,10 @@
                     return KeymasterDefs.KM_ALGORITHM_AES;
                 case HMAC:
                     return KeymasterDefs.KM_ALGORITHM_HMAC;
+                case RSA:
+                    return KeymasterDefs.KM_ALGORITHM_RSA;
+                case EC:
+                    return KeymasterDefs.KM_ALGORITHM_ECDSA;
                 default:
                     throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
             }
@@ -165,6 +179,10 @@
                     return AES;
                 case KeymasterDefs.KM_ALGORITHM_HMAC:
                     return HMAC;
+                case KeymasterDefs.KM_ALGORITHM_RSA:
+                    return RSA;
+                case KeymasterDefs.KM_ALGORITHM_ECDSA:
+                    return EC;
                 default:
                     throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
             }
@@ -179,6 +197,10 @@
                     return "AES";
                 case HMAC:
                     return "HMAC";
+                case RSA:
+                    return "RSA";
+                case EC:
+                    return "EC";
                 default:
                     throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
             }
@@ -213,8 +235,18 @@
                         throw new IllegalArgumentException("HMAC digest not specified");
                     }
                     switch (digest) {
+                        case Digest.MD5:
+                            return "HmacMD5";
+                        case Digest.SHA1:
+                            return "HmacSHA1";
+                        case Digest.SHA224:
+                            return "HmacSHA224";
                         case Digest.SHA256:
                             return "HmacSHA256";
+                        case Digest.SHA384:
+                            return "HmacSHA384";
+                        case Digest.SHA512:
+                            return "HmacSHA512";
                         default:
                             throw new IllegalArgumentException(
                                     "Unsupported HMAC digest: " + digest);
@@ -223,11 +255,32 @@
                     throw new IllegalArgumentException("Unsupported key algorithm: " + algorithm);
             }
         }
+
+        /**
+         * @hide
+         */
+        public static String toJCAKeyPairAlgorithm(@AlgorithmEnum int algorithm) {
+            switch (algorithm) {
+                case RSA:
+                    return "RSA";
+                case EC:
+                    return "EC";
+                default:
+                    throw new IllegalArgumentException("Unsupported key alorithm: " + algorithm);
+            }
+        }
     }
 
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
-            value = {Padding.NONE, Padding.PKCS7})
+            value = {
+                Padding.NONE,
+                Padding.PKCS7,
+                Padding.RSA_PKCS1_ENCRYPTION,
+                Padding.RSA_PKCS1_SIGNATURE,
+                Padding.RSA_OAEP,
+                Padding.RSA_PSS,
+                })
     public @interface PaddingEnum {}
 
     /**
@@ -247,6 +300,26 @@
         public static final int PKCS7 = 1 << 1;
 
         /**
+         * RSA PKCS#1 v1.5 padding for encryption/decryption.
+         */
+        public static final int RSA_PKCS1_ENCRYPTION = 1 << 2;
+
+        /**
+         * RSA PKCS#1 v1.5 padding for signatures.
+         */
+        public static final int RSA_PKCS1_SIGNATURE = 1 << 3;
+
+        /**
+         * RSA Optimal Asymmetric Encryption Padding (OAEP).
+         */
+        public static final int RSA_OAEP = 1 << 4;
+
+        /**
+         * RSA PKCS#1 v2.1 Probabilistic Signature Scheme (PSS) padding.
+         */
+        public static final int RSA_PSS = 1 << 5;
+
+        /**
          * @hide
          */
         public static int toKeymaster(int padding) {
@@ -255,6 +328,14 @@
                     return KeymasterDefs.KM_PAD_NONE;
                 case PKCS7:
                     return KeymasterDefs.KM_PAD_PKCS7;
+                case RSA_PKCS1_ENCRYPTION:
+                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT;
+                case RSA_PKCS1_SIGNATURE:
+                    return KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN;
+                case RSA_OAEP:
+                    return KeymasterDefs.KM_PAD_RSA_OAEP;
+                case RSA_PSS:
+                    return KeymasterDefs.KM_PAD_RSA_PSS;
                 default:
                     throw new IllegalArgumentException("Unknown padding: " + padding);
             }
@@ -269,6 +350,14 @@
                     return NONE;
                 case KeymasterDefs.KM_PAD_PKCS7:
                     return PKCS7;
+                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
+                    return RSA_PKCS1_ENCRYPTION;
+                case KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN:
+                    return RSA_PKCS1_SIGNATURE;
+                case KeymasterDefs.KM_PAD_RSA_OAEP:
+                    return RSA_OAEP;
+                case KeymasterDefs.KM_PAD_RSA_PSS:
+                    return RSA_PSS;
                 default:
                     throw new IllegalArgumentException("Unknown padding: " + padding);
             }
@@ -283,6 +372,14 @@
                     return "NONE";
                 case PKCS7:
                     return "PKCS#7";
+                case RSA_PKCS1_ENCRYPTION:
+                    return "RSA PKCS#1 (encryption)";
+                case RSA_PKCS1_SIGNATURE:
+                    return "RSA PKCS#1 (signature)";
+                case RSA_OAEP:
+                    return "RSA OAEP";
+                case RSA_PSS:
+                    return "RSA PSS";
                 default:
                     throw new IllegalArgumentException("Unknown padding: " + padding);
             }
@@ -291,12 +388,18 @@
         /**
          * @hide
          */
-        public static @PaddingEnum int fromJCAPadding(String padding) {
+        public static @PaddingEnum int fromJCACipherPadding(String padding) {
             String paddingLower = padding.toLowerCase(Locale.US);
             if ("nopadding".equals(paddingLower)) {
                 return NONE;
             } else if ("pkcs7padding".equals(paddingLower)) {
                 return PKCS7;
+            } else if ("pkcs1padding".equals(paddingLower)) {
+                return RSA_PKCS1_ENCRYPTION;
+            } else if (("oaeppadding".equals(paddingLower))
+                    || ((paddingLower.startsWith("oaepwith"))
+                            && (paddingLower.endsWith("padding")))) {
+                return RSA_OAEP;
             } else {
                 throw new IllegalArgumentException("Unknown padding: " + padding);
             }
@@ -592,6 +695,14 @@
         public static final int GCM = 1 << 3;
 
         /**
+         * Set of block modes compatible with IND-CPA if used correctly.
+         *
+         * @hide
+         */
+        public static final @BlockModeEnum int IND_CPA_COMPATIBLE_MODES =
+                CBC | CTR | GCM;
+
+        /**
          * @hide
          */
         public static int toKeymaster(@BlockModeEnum int mode) {
@@ -670,6 +781,24 @@
         /**
          * @hide
          */
+        public static String allToString(@BlockModeEnum int modes) {
+            StringBuilder result = new StringBuilder("[");
+            boolean firstValue = true;
+            for (@BlockModeEnum int mode : getSetFlags(modes)) {
+                if (firstValue) {
+                    firstValue = false;
+                } else {
+                    result.append(", ");
+                }
+                result.append(toString(mode));
+            }
+            result.append(']');
+            return result.toString();
+        }
+
+        /**
+         * @hide
+         */
         public static @BlockModeEnum int fromJCAMode(String mode) {
             String modeLower = mode.toLowerCase(Locale.US);
             if ("ecb".equals(modeLower)) {
diff --git a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
index 5be8c39..a500786 100644
--- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java
@@ -138,13 +138,26 @@
         }
         int keySizeBits = (spec.getKeySize() != null) ? spec.getKeySize() : mDefaultKeySizeBits;
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, keySizeBits);
-        int purposes = spec.getPurposes();
+        @KeyStoreKeyConstraints.PurposeEnum int purposes = spec.getPurposes();
+        @KeyStoreKeyConstraints.BlockModeEnum int blockModes = spec.getBlockModes();
+        if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
+                && (spec.isRandomizedEncryptionRequired())) {
+            @KeyStoreKeyConstraints.BlockModeEnum int incompatibleBlockModes =
+                    blockModes & ~KeyStoreKeyConstraints.BlockMode.IND_CPA_COMPATIBLE_MODES;
+            if (incompatibleBlockModes != 0) {
+                throw new IllegalStateException(
+                        "Randomized encryption (IND-CPA) required but may be violated by block"
+                        + " mode(s): "
+                        + KeyStoreKeyConstraints.BlockMode.allToString(incompatibleBlockModes)
+                        + ". See KeyGeneratorSpec documentation.");
+            }
+        }
+
         for (int keymasterPurpose :
             KeyStoreKeyConstraints.Purpose.allToKeymaster(purposes)) {
             args.addInt(KeymasterDefs.KM_TAG_PURPOSE, keymasterPurpose);
         }
-        for (int keymasterBlockMode :
-            KeyStoreKeyConstraints.BlockMode.allToKeymaster(spec.getBlockModes())) {
+        for (int keymasterBlockMode : KeyStoreKeyConstraints.BlockMode.allToKeymaster(blockModes)) {
             args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, keymasterBlockMode);
         }
         for (int keymasterPadding :
@@ -173,8 +186,8 @@
                 ? spec.getKeyValidityForConsumptionEnd() : new Date(Long.MAX_VALUE));
 
         if (((purposes & KeyStoreKeyConstraints.Purpose.ENCRYPT) != 0)
-            || ((purposes & KeyStoreKeyConstraints.Purpose.DECRYPT) != 0)) {
-            // Permit caller-specified IV. This is needed due to the Cipher abstraction.
+                && (!spec.isRandomizedEncryptionRequired())) {
+            // Permit caller-provided IV when encrypting with this key
             args.addBoolean(KeymasterDefs.KM_TAG_CALLER_NONCE);
         }
 
diff --git a/keystore/java/android/security/KeyStoreParameter.java b/keystore/java/android/security/KeyStoreParameter.java
index 4909467..fb534b4 100644
--- a/keystore/java/android/security/KeyStoreParameter.java
+++ b/keystore/java/android/security/KeyStoreParameter.java
@@ -19,13 +19,14 @@
 import android.content.Context;
 
 import java.security.Key;
-import java.security.KeyPairGenerator;
 import java.security.KeyStore.ProtectionParameter;
 import java.util.Date;
 
+import javax.crypto.Cipher;
+
 /**
- * This provides the optional parameters that can be specified for
- * {@code KeyStore} entries that work with
+ * Parameters specifying how to secure and restrict the use of a key being
+ * imported into the
  * <a href="{@docRoot}training/articles/keystore.html">Android KeyStore
  * facility</a>. The Android KeyStore facility is accessed through a
  * {@link java.security.KeyStore} API using the {@code AndroidKeyStore}
@@ -36,12 +37,6 @@
  * there is only one logical instance of the {@code KeyStore} per application
  * UID so apps using the {@code sharedUid} facility will also share a
  * {@code KeyStore}.
- * <p>
- * Keys may be generated using the {@link KeyPairGenerator} facility with a
- * {@link KeyPairGeneratorSpec} to specify the entry's {@code alias}. A
- * self-signed X.509 certificate will be attached to generated entries, but that
- * may be replaced at a later time by a certificate signed by a real Certificate
- * Authority.
  */
 public final class KeyStoreParameter implements ProtectionParameter {
     private int mFlags;
@@ -52,6 +47,7 @@
     private final @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
     private final @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
     private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+    private final boolean mRandomizedEncryptionRequired;
     private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
     private final int mUserAuthenticationValidityDurationSeconds;
 
@@ -63,6 +59,7 @@
             @KeyStoreKeyConstraints.PaddingEnum int paddings,
             @KeyStoreKeyConstraints.DigestEnum Integer digests,
             @KeyStoreKeyConstraints.BlockModeEnum int blockModes,
+            boolean randomizedEncryptionRequired,
             @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators,
             int userAuthenticationValidityDurationSeconds) {
         if ((userAuthenticationValidityDurationSeconds < 0)
@@ -79,6 +76,7 @@
         mPaddings = paddings;
         mDigests = digests;
         mBlockModes = blockModes;
+        mRandomizedEncryptionRequired = randomizedEncryptionRequired;
         mUserAuthenticators = userAuthenticators;
         mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds;
     }
@@ -185,6 +183,21 @@
     }
 
     /**
+     * Returns {@code true} if encryption using this key must be sufficiently randomized to produce
+     * different ciphertexts for the same plaintext every time. The formal cryptographic property
+     * being required is <em>indistinguishability under chosen-plaintext attack ({@code
+     * IND-CPA})</em>. This property is important because it mitigates several classes of
+     * weaknesses due to which ciphertext may leak information about plaintext. For example, if a
+     * given plaintext always produces the same ciphertext, an attacker may see the repeated
+     * ciphertexts and be able to deduce something about the plaintext.
+     *
+     * @hide
+     */
+    public boolean isRandomizedEncryptionRequired() {
+        return mRandomizedEncryptionRequired;
+    }
+
+    /**
      * Gets the set of user authenticators which protect access to this key. The key can only be
      * used iff the user has authenticated to at least one of these user authenticators.
      *
@@ -235,6 +248,7 @@
         private @KeyStoreKeyConstraints.PaddingEnum int mPaddings;
         private @KeyStoreKeyConstraints.DigestEnum Integer mDigests;
         private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes;
+        private boolean mRandomizedEncryptionRequired = true;
         private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators;
         private int mUserAuthenticationValidityDurationSeconds = -1;
 
@@ -270,7 +284,7 @@
         /**
          * Sets the time instant before which the key is not yet valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityEnd(Date)
          *
@@ -284,7 +298,7 @@
         /**
          * Sets the time instant after which the key is no longer valid.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityStart(Date)
          * @see #setKeyValidityForConsumptionEnd(Date)
@@ -301,7 +315,7 @@
         /**
          * Sets the time instant after which the key is no longer valid for encryption and signing.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForConsumptionEnd(Date)
          *
@@ -316,7 +330,7 @@
          * Sets the time instant after which the key is no longer valid for decryption and
          * verification.
          *
-         * <b>By default, the key is valid at any instant.
+         * <p>By default, the key is valid at any instant.
          *
          * @see #setKeyValidityForOriginationEnd(Date)
          *
@@ -381,6 +395,47 @@
         }
 
         /**
+         * Sets whether encryption using this key must be sufficiently randomized to produce
+         * different ciphertexts for the same plaintext every time. The formal cryptographic
+         * property being required is <em>indistinguishability under chosen-plaintext attack
+         * ({@code IND-CPA})</em>. This property is important because it mitigates several classes
+         * of weaknesses due to which ciphertext may leak information about plaintext. For example,
+         * if a given plaintext always produces the same ciphertext, an attacker may see the
+         * repeated ciphertexts and be able to deduce something about the plaintext.
+         *
+         * <p>By default, {@code IND-CPA} is required.
+         *
+         * <p>When {@code IND-CPA} is required:
+         * <ul>
+         * <li>transformation which do not offer {@code IND-CPA}, such as symmetric ciphers using
+         * {@code ECB} mode or RSA encryption without padding, are prohibited;</li>
+         * <li>in transformations which use an IV, such as symmetric ciphers in {@code CBC},
+         * {@code CTR}, and {@code GCM} block modes, caller-provided IVs are rejected when
+         * encrypting, to ensure that only random IVs are used.</li>
+         *
+         * <p>Before disabling this requirement, consider the following approaches instead:
+         * <ul>
+         * <li>If you are generating a random IV for encryption and then initializing a {@code}
+         * Cipher using the IV, the solution is to let the {@code Cipher} generate a random IV
+         * instead. This will occur if the {@code Cipher} is initialized for encryption without an
+         * IV. The IV can then be queried via {@link Cipher#getIV()}.</li>
+         * <li>If you are generating a non-random IV (e.g., an IV derived from something not fully
+         * random, such as the name of the file being encrypted, or transaction ID, or password,
+         * or a device identifier), consider changing your design to use a random IV which will then
+         * be provided in addition to the ciphertext to the entities which need to decrypt the
+         * ciphertext.</li>
+         * <li>If you are using RSA encryption without padding, consider switching to padding
+         * schemes which offer {@code IND-CPA}, such as PKCS#1 or OAEP.</li>
+         * </ul>
+         *
+         * @hide
+         */
+        public Builder setRandomizedEncryptionRequired(boolean required) {
+            mRandomizedEncryptionRequired = required;
+            return this;
+        }
+
+        /**
          * Sets the user authenticators which protect access to this key. The key can only be used
          * iff the user has authenticated to at least one of these user authenticators.
          *
@@ -432,6 +487,7 @@
                     mPaddings,
                     mDigests,
                     mBlockModes,
+                    mRandomizedEncryptionRequired,
                     mUserAuthenticators,
                     mUserAuthenticationValidityDurationSeconds);
         }
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 74570e6..ce31f98 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -72,43 +72,25 @@
         return mInstaller.execute(builder.toString());
     }
 
-    public int patchoat(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet) {
+    public int dexopt(String apkPath, int uid, boolean isPublic,
+            String instructionSet, int dexoptNeeded) {
         if (!isValidInstructionSet(instructionSet)) {
             Slog.e(TAG, "Invalid instruction set: " + instructionSet);
             return -1;
         }
 
-        return mInstaller.patchoat(apkPath, uid, isPublic, pkgName, instructionSet);
-    }
-
-    public int patchoat(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        return mInstaller.patchoat(apkPath, uid, isPublic, instructionSet);
-    }
-
-    public int dexopt(String apkPath, int uid, boolean isPublic, String instructionSet) {
-        if (!isValidInstructionSet(instructionSet)) {
-            Slog.e(TAG, "Invalid instruction set: " + instructionSet);
-            return -1;
-        }
-
-        return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet);
+        return mInstaller.dexopt(apkPath, uid, isPublic, instructionSet, dexoptNeeded);
     }
 
     public int dexopt(String apkPath, int uid, boolean isPublic, String pkgName,
-            String instructionSet, boolean vmSafeMode, boolean debuggable,
-            @Nullable String outputPath) {
+            String instructionSet, int dexoptNeeded, boolean vmSafeMode,
+            boolean debuggable, @Nullable String outputPath) {
         if (!isValidInstructionSet(instructionSet)) {
             Slog.e(TAG, "Invalid instruction set: " + instructionSet);
             return -1;
         }
-
-        return mInstaller.dexopt(apkPath, uid, isPublic, pkgName, instructionSet, vmSafeMode,
+        return mInstaller.dexopt(apkPath, uid, isPublic, pkgName,
+                instructionSet, dexoptNeeded, vmSafeMode,
                 debuggable, outputPath);
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 680ec4b..4c36fa6 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -113,64 +113,48 @@
 
             for (String path : paths) {
                 try {
-                    // This will return DEXOPT_NEEDED if we either cannot find any odex file for this
-                    // package or the one we find does not match the image checksum (i.e. it was
-                    // compiled against an old image). It will return PATCHOAT_NEEDED if we can find a
-                    // odex file and it matches the checksum of the image but not its base address,
-                    // meaning we need to move it.
-                    final byte isDexOptNeeded = DexFile.isDexOptNeededInternal(path,
-                            pkg.packageName, dexCodeInstructionSet, defer);
-                    if (forceDex || (!defer && isDexOptNeeded == DexFile.DEXOPT_NEEDED)) {
-                        File oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
-                        Log.i(TAG, "Running dexopt on: " + path + " pkg="
+                    final int dexoptNeeded;
+                    if (forceDex) {
+                        dexoptNeeded = DexFile.DEX2OAT_NEEDED;
+                    } else {
+                        dexoptNeeded = DexFile.getDexOptNeeded(path,
+                                pkg.packageName, dexCodeInstructionSet, defer);
+                    }
+
+                    if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                        // We're deciding to defer a needed dexopt. Don't bother dexopting for other
+                        // paths and instruction sets. We'll deal with them all together when we process
+                        // our list of deferred dexopts.
+                        addPackageForDeferredDexopt(pkg);
+                        return DEX_OPT_DEFERRED;
+                    }
+
+                    if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                        final String dexoptType;
+                        String oatDir = null;
+                        if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {
+                            dexoptType = "dex2oat";
+                            oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
+                        } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {
+                            dexoptType = "patchoat";
+                        } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {
+                            dexoptType = "self patchoat";
+                        } else {
+                            throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);
+                        }
+                        Log.i(TAG, "Running dexopt (" + dexoptType + ") on: " + path + " pkg="
                                 + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet
                                 + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable
                                 + " oatDir = " + oatDir);
                         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-
-                        if (oatDir != null) {
-                            int ret = mPackageManagerService.mInstaller.dexopt(
-                                    path, sharedGid, !pkg.isForwardLocked(), pkg.packageName,
-                                    dexCodeInstructionSet, vmSafeMode, debuggable,
-                                    oatDir.getAbsolutePath());
-                            if (ret < 0) {
-                                return DEX_OPT_FAILED;
-                            }
-                        } else {
-                            final int ret = mPackageManagerService.mInstaller
-                                    .dexopt(path, sharedGid,
-                                            !pkg.isForwardLocked(), pkg.packageName,
-                                            dexCodeInstructionSet,
-                                            vmSafeMode, debuggable, null);
-                            if (ret < 0) {
-                                return DEX_OPT_FAILED;
-                            }
-                        }
-
-                        performedDexOpt = true;
-                    } else if (!defer && isDexOptNeeded == DexFile.PATCHOAT_NEEDED) {
-                        Log.i(TAG, "Running patchoat on: " + pkg.applicationInfo.packageName);
-                        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-                        final int ret = mPackageManagerService.mInstaller.patchoat(path, sharedGid,
-                                !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet);
-
+                        final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,
+                                !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,
+                                dexoptNeeded, vmSafeMode, debuggable, oatDir);
                         if (ret < 0) {
-                            // Don't bother running patchoat again if we failed, it will probably
-                            // just result in an error again. Also, don't bother dexopting for other
-                            // paths & ISAs.
                             return DEX_OPT_FAILED;
                         }
-
                         performedDexOpt = true;
                     }
-
-                    // We're deciding to defer a needed dexopt. Don't bother dexopting for other
-                    // paths and instruction sets. We'll deal with them all together when we process
-                    // our list of deferred dexopts.
-                    if (defer && isDexOptNeeded != DexFile.UP_TO_DATE) {
-                        addPackageForDeferredDexopt(pkg);
-                        return DEX_OPT_DEFERRED;
-                    }
                 } catch (FileNotFoundException e) {
                     Slog.w(TAG, "Apk not found for dexopt: " + path);
                     return DEX_OPT_FAILED;
@@ -187,7 +171,7 @@
             }
 
             // At this point we haven't failed dexopt and we haven't deferred dexopt. We must
-            // either have either succeeded dexopt, or have had isDexOptNeededInternal tell us
+            // either have either succeeded dexopt, or have had getDexOptNeeded tell us
             // it isn't required. We therefore mark that this package doesn't need dexopt unless
             // it's forced. performedDexOpt will tell us whether we performed dex-opt or skipped
             // it.
@@ -209,10 +193,11 @@
      *      <li>Package location is not a directory, i.e. monolithic install.</li>
      * </ul>
      *
-     * @return oat directory or null, if oat directory cannot be created.
+     * @return Absolute path to the oat directory or null, if oat directory
+     * cannot be created.
      */
     @Nullable
-    private File createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
+    private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)
             throws IOException {
         if (pkg.isSystemApp() && !pkg.isUpdatedSystemApp()) {
             return null;
@@ -222,7 +207,7 @@
             File oatDir = getOatDir(codePath);
             mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
                     dexInstructionSet);
-            return oatDir;
+            return oatDir.getAbsolutePath();
         }
         return null;
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index bb8a785..3f0e8b0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1457,18 +1457,10 @@
                         }
 
                         try {
-                            byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null,
-                                                                                 dexCodeInstructionSet,
-                                                                                 false);
-                            if (dexoptRequired != DexFile.UP_TO_DATE) {
+                            int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);
+                            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
                                 alreadyDexOpted.add(lib);
-
-                                // The list of "shared libraries" we have at this point is
-                                if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
-                                    mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
-                                } else {
-                                    mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);
-                                }
+                                mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Library not found: " + lib);
@@ -1514,13 +1506,9 @@
                             continue;
                         }
                         try {
-                            byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null,
-                                                                                 dexCodeInstructionSet,
-                                                                                 false);
-                            if (dexoptRequired == DexFile.DEXOPT_NEEDED) {
-                                mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
-                            } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) {
-                                mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);
+                            int dexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet, false);
+                            if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
+                                mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);
                             }
                         } catch (FileNotFoundException e) {
                             Slog.w(TAG, "Jar not found: " + path);