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);