diff options
13 files changed, 436 insertions, 163 deletions
diff --git a/core/java/com/android/internal/os/InstallerConnection.java b/core/java/com/android/internal/os/InstallerConnection.java index a4cdf194189f..671bf2444acc 100644 --- a/core/java/com/android/internal/os/InstallerConnection.java +++ b/core/java/com/android/internal/os/InstallerConnection.java @@ -90,12 +90,15 @@ public class InstallerConnection { } } - 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 @@ public class InstallerConnection { 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 @@ public class InstallerConnection { 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 98638ed2cf75..50ddbd16ba39 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -465,12 +465,11 @@ public class ZygoteInit { 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 86732196518c..0822afd59bfb 100644 --- a/include/androidfw/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -112,7 +112,7 @@ struct __assertChar16Size { * * 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 f72c7acd182b..55a8b4f971f3 100644 --- a/keystore/java/android/security/AndroidKeyStore.java +++ b/keystore/java/android/security/AndroidKeyStore.java @@ -512,12 +512,23 @@ public class AndroidKeyStore extends KeyStoreSpi { } } - 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 @@ public class AndroidKeyStore extends KeyStoreSpi { 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 635b2fa94b09..43f3b30fa4fb 100644 --- a/keystore/java/android/security/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/AndroidKeyStoreProvider.java @@ -64,6 +64,7 @@ public class AndroidKeyStoreProvider extends Provider { 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 3ada7f6e5781..4dcabd030f98 100644 --- a/keystore/java/android/security/KeyGeneratorSpec.java +++ b/keystore/java/android/security/KeyGeneratorSpec.java @@ -22,6 +22,7 @@ import android.text.TextUtils; 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { @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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mPurposes = purposes; mPaddings = paddings; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -169,6 +173,19 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 @@ public class KeyGeneratorSpec implements AlgorithmParameterSpec { mPurposes, mPaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyPairGeneratorSpec.java b/keystore/java/android/security/KeyPairGeneratorSpec.java index edaa9a5444c6..db310ea2bc12 100644 --- a/keystore/java/android/security/KeyPairGeneratorSpec.java +++ b/keystore/java/android/security/KeyPairGeneratorSpec.java @@ -86,6 +86,8 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private final @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private final boolean mRandomizedEncryptionRequired; + private final @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private final int mUserAuthenticationValidityDurationSeconds; @@ -132,6 +134,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { @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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mDigests = digests; mPaddings = paddings; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -182,8 +186,26 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { private @KeyStoreKeyConstraints.BlockModeEnum int mBlockModes; + private boolean mRandomizedEncryptionRequired = true; + private @KeyStoreKeyConstraints.UserAuthenticatorEnum int mUserAuthenticators; private int mUserAuthenticationValidityDurationSeconds = -1; @@ -561,7 +600,7 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { /** * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { } /** + * 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 @@ public final class KeyPairGeneratorSpec implements AlgorithmParameterSpec { mDigests, mPaddings, mBlockModes, + mRandomizedEncryptionRequired, mUserAuthenticators, mUserAuthenticationValidityDurationSeconds); } diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java index 9321302c124b..cde27f928644 100644 --- a/keystore/java/android/security/KeyStoreKeyConstraints.java +++ b/keystore/java/android/security/KeyStoreKeyConstraints.java @@ -123,7 +123,7 @@ public abstract class KeyStoreKeyConstraints { } @Retention(RetentionPolicy.SOURCE) - @IntDef({Algorithm.AES, Algorithm.HMAC}) + @IntDef({Algorithm.AES, Algorithm.HMAC, Algorithm.RSA, Algorithm.EC}) public @interface AlgorithmEnum {} /** @@ -135,12 +135,22 @@ public abstract class KeyStoreKeyConstraints { /** * 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 abstract class KeyStoreKeyConstraints { 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 @@ public abstract class KeyStoreKeyConstraints { /** * @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 5be8c394f2be..a500786cbc47 100644 --- a/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java +++ b/keystore/java/android/security/KeyStoreKeyGeneratorSpi.java @@ -138,13 +138,26 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { } 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 @@ public abstract class KeyStoreKeyGeneratorSpi extends KeyGeneratorSpi { ? 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 49094677bf51..fb534b46f6cb 100644 --- a/keystore/java/android/security/KeyStoreParameter.java +++ b/keystore/java/android/security/KeyStoreParameter.java @@ -19,13 +19,14 @@ package android.security; 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 @@ import java.util.Date; * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @@ public final class KeyStoreParameter implements ProtectionParameter { @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 @@ public final class KeyStoreParameter implements ProtectionParameter { mPaddings = paddings; mDigests = digests; mBlockModes = blockModes; + mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticators = userAuthenticators; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; } @@ -185,6 +183,21 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { /** * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { } /** + * 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 @@ public final class KeyStoreParameter implements ProtectionParameter { 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 74570e6c87bc..ce31f98a6f50 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 @@ public final class Installer extends SystemService { 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 680ec4b4bb87..4c36fa6eea65 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 @@ final class PackageDexOptimizer { 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=" - + pkg.applicationInfo.packageName + " isa=" + dexCodeInstructionSet - + " vmSafeMode=" + vmSafeMode + " debuggable=" + debuggable - + " oatDir = " + oatDir); - final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); + final int dexoptNeeded; + if (forceDex) { + dexoptNeeded = DexFile.DEX2OAT_NEEDED; + } else { + dexoptNeeded = DexFile.getDexOptNeeded(path, + pkg.packageName, dexCodeInstructionSet, defer); + } - 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; - } + 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 { - final int ret = mPackageManagerService.mInstaller - .dexopt(path, sharedGid, - !pkg.isForwardLocked(), pkg.packageName, - dexCodeInstructionSet, - vmSafeMode, debuggable, null); - if (ret < 0) { - return DEX_OPT_FAILED; - } + throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded); } - - performedDexOpt = true; - } else if (!defer && isDexOptNeeded == DexFile.PATCHOAT_NEEDED) { - Log.i(TAG, "Running patchoat on: " + pkg.applicationInfo.packageName); + 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); - 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 @@ final class PackageDexOptimizer { } // 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 @@ final class PackageDexOptimizer { * <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 @@ final class PackageDexOptimizer { 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 bb8a78575014..3f0e8b001be5 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 @@ public class PackageManagerService extends IPackageManager.Stub { } 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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); |