Merge "Clean-up state if we have an exception when acquiring provider"
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 6176399..d3953b3 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -47,21 +47,17 @@
     public static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
     public static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
     public static final int KM_TAG_KEY_SIZE = KM_INT | 3;
-    public static final int KM_TAG_BLOCK_MODE = KM_ENUM | 4;
-    public static final int KM_TAG_DIGEST = KM_ENUM | 5;
-    public static final int KM_TAG_MAC_LENGTH = KM_INT | 6;
-    public static final int KM_TAG_PADDING = KM_ENUM | 7;
-    public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 8;
-    public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 9;
+    public static final int KM_TAG_BLOCK_MODE = KM_ENUM_REP | 4;
+    public static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
+    public static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
+    public static final int KM_TAG_RETURN_UNAUTHED = KM_BOOL | 7;
+    public static final int KM_TAG_CALLER_NONCE = KM_BOOL | 8;
 
     public static final int KM_TAG_RESCOPING_ADD = KM_ENUM_REP | 101;
     public static final int KM_TAG_RESCOPING_DEL = KM_ENUM_REP | 102;
     public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705;
 
     public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200;
-    public static final int KM_TAG_DSA_GENERATOR = KM_BIGNUM | 201;
-    public static final int KM_TAG_DSA_P = KM_BIGNUM | 202;
-    public static final int KM_TAG_DSA_Q = KM_BIGNUM | 203;
     public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
     public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
     public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
@@ -88,43 +84,21 @@
     public static final int KM_TAG_NONCE = KM_BYTES | 1001;
     public static final int KM_TAG_CHUNK_LENGTH = KM_INT | 1002;
     public static final int KM_TAG_AUTH_TOKEN = KM_BYTES | 1003;
+    public static final int KM_TAG_MAC_LENGTH = KM_INT | 1004;
 
     // Algorithm values.
     public static final int KM_ALGORITHM_RSA = 1;
-    public static final int KM_ALGORITHM_DSA = 2;
-    public static final int KM_ALGORITHM_ECDSA = 3;
-    public static final int KM_ALGORITHM_ECIES = 4;
+    public static final int KM_ALGORITHM_EC = 3;
     public static final int KM_ALGORITHM_AES = 32;
-    public static final int KM_ALGORITHM_3DES = 33;
-    public static final int KM_ALGORITHM_SKIPJACK = 34;
-    public static final int KM_ALGORITHM_MARS = 48;
-    public static final int KM_ALGORITHM_RC6 = 49;
-    public static final int KM_ALGORITHM_SERPENT = 50;
-    public static final int KM_ALGORITHM_TWOFISH = 51;
-    public static final int KM_ALGORITHM_IDEA = 52;
-    public static final int KM_ALGORITHM_RC5 = 53;
-    public static final int KM_ALGORITHM_CAST5 = 54;
-    public static final int KM_ALGORITHM_BLOWFISH = 55;
-    public static final int KM_ALGORITHM_RC4 = 64;
-    public static final int KM_ALGORITHM_CHACHA20 = 65;
     public static final int KM_ALGORITHM_HMAC = 128;
 
     // Block modes.
     public static final int KM_MODE_FIRST_UNAUTHENTICATED = 1;
     public static final int KM_MODE_ECB = KM_MODE_FIRST_UNAUTHENTICATED;
     public static final int KM_MODE_CBC = 2;
-    public static final int KM_MODE_CBC_CTS = 3;
     public static final int KM_MODE_CTR = 4;
-    public static final int KM_MODE_OFB = 5;
-    public static final int KM_MODE_CFB = 6;
-    public static final int KM_MODE_XTS = 7;
     public static final int KM_MODE_FIRST_AUTHENTICATED = 32;
     public static final int KM_MODE_GCM = KM_MODE_FIRST_AUTHENTICATED;
-    public static final int KM_MODE_OCB = 33;
-    public static final int KM_MODE_CCM = 34;
-    public static final int KM_MODE_FIRST_MAC = 128;
-    public static final int KM_MODE_CMAC = KM_MODE_FIRST_MAC;
-    public static final int KM_MODE_POLY1305 = 129;
 
     // Padding modes.
     public static final int KM_PAD_NONE = 1;
@@ -132,11 +106,7 @@
     public static final int KM_PAD_RSA_PSS = 3;
     public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;
     public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;
-    public static final int KM_PAD_ANSI_X923 = 32;
-    public static final int KM_PAD_ISO_10126 = 33;
-    public static final int KM_PAD_ZERO = 64;
-    public static final int KM_PAD_PKCS7 = 65;
-    public static final int KM_PAD_ISO_7816_4 = 66;
+    public static final int KM_PAD_PKCS7 = 64;
 
     // Digest modes.
     public static final int KM_DIGEST_NONE = 0;
@@ -146,9 +116,6 @@
     public static final int KM_DIGEST_SHA_2_256 = 4;
     public static final int KM_DIGEST_SHA_2_384 = 5;
     public static final int KM_DIGEST_SHA_2_512 = 6;
-    public static final int KM_DIGEST_SHA_3_256 = 7;
-    public static final int KM_DIGEST_SHA_3_384 = 8;
-    public static final int KM_DIGEST_SHA_3_512 = 9;
 
     // Key origins.
     public static final int KM_ORIGIN_HARDWARE = 0;
@@ -168,7 +135,6 @@
     // Key formats.
     public static final int KM_KEY_FORMAT_X509 = 0;
     public static final int KM_KEY_FORMAT_PKCS8 = 1;
-    public static final int KM_KEY_FORMAT_PKCS12 = 2;
     public static final int KM_KEY_FORMAT_RAW = 3;
 
     // User authenticators.
@@ -218,7 +184,6 @@
     public static final int KM_ERROR_INVALID_TAG = -40;
     public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41;
     public static final int KM_ERROR_INVALID_RESCOPING = -42;
-    public static final int KM_ERROR_INVALID_DSA_PARAMS = -43;
     public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44;
     public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45;
     public static final int KM_ERROR_OPERATION_CANCELLED = -46;
diff --git a/keystore/java/android/security/EcIesParameterSpec.java b/keystore/java/android/security/EcIesParameterSpec.java
new file mode 100644
index 0000000..0f19812
--- /dev/null
+++ b/keystore/java/android/security/EcIesParameterSpec.java
@@ -0,0 +1,287 @@
+package android.security;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+
+/**
+ * {@link AlgorithmParameterSpec} for ECIES (Integrated Encryption Scheme using Elliptic Curve
+ * cryptography) based on {@code ISO/IEC 18033-2}.
+ *
+ * <p>ECIES is a hybrid authenticated encryption scheme. Encryption is performed using an Elliptic
+ * Curve (EC) public key. The resulting ciphertext can be decrypted only using the corresponding EC
+ * private key. The scheme is called hybrid because the EC key is only used to securely encapsulate
+ * symmetric key material. Encryption of plaintext and authentication of the corresponding
+ * ciphertext is performed using symmetric cryptography.
+ *
+ * <p>Encryption using ECIES consists of two stages:
+ * <ol>
+ * <li>Key Encapsulation Mechanism (KEM) randomly generates symmetric key material and securely
+ * encapsulates it in the output so that it can be extracted by the KEM when decrypting.
+ * Encapsulated key material is represented in the output as an EC point.</li>
+ * <li>The above symmetric key material is used by Data Encapsulation Mechanism (DEM) to encrypt the
+ * provided plaintext and authenticate the ciphertext. The resulting authenticated ciphertext is
+ * then output. When decrypting, the DEM first authenticates the ciphertext and, only if it
+ * authenticates, decrypts the ciphertext and outputs the plaintext.</li>
+ * </ol>
+ *
+ * <p>Details of KEM:
+ * <ul>
+ * <li>Only curves with cofactor of {@code 1} are supported.</li>
+ * <li>{@code CheckMode}, {@code OldCofactorMode}, {@code CofactorMode}, and {@code SingleHashMode}
+ * are {@code 0}.
+ * <li>Point format is specified by {@link #getKemPointFormat()}.</li>
+ * <li>KDF algorithm is specified by {@link #getKemKdfAlgorithm()}.</li>
+ * </ul>
+ *
+ * <p>Details of DEM:
+ * <ul>
+ * <li>Only DEM1-like mechanism is supported, with its symmetric cipher (SC) specified by
+ * {@link #getDemCipherTransformation()} (e.g., {@code AES/CBC/NoPadding} for standard DEM1) and
+ * MAC algorithm specified by {@link #getDemMacAlgorithm()} (e.g., {@code HmacSHA1} for standard
+ * DEM1).</li>
+ * </ul>
+ *
+ * @hide
+ */
+public class EcIesParameterSpec implements AlgorithmParameterSpec {
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {PointFormat.UNCOMPRESSED, PointFormat.COMPRESSED})
+    public @interface PointFormatEnum {}
+
+    /**
+     * Wire format of the EC point.
+     */
+    public static abstract class PointFormat {
+
+        private PointFormat() {}
+
+        /** Unspecified point format. */
+        public static final int UNSPECIFIED = -1;
+
+        /**
+         * Uncompressed point format: both coordinates are stored separately.
+         *
+         * <p>The wire format is byte {@code 0x04} followed by binary representation of the
+         * {@code x} coordinate followed by binary representation of the {@code y} coordinate. See
+         * {@code ISO 18033-2} section {@code 5.4.3}.
+         */
+        public static final int UNCOMPRESSED = 0;
+
+        /**
+         * Compressed point format: only one coordinate is stored.
+         *
+         * <p>The wire format is byte {@code 0x02} or {@code 0x03} (depending on the value of the
+         * stored coordinate) followed by the binary representation of the {@code x} coordinate.
+         * See {@code ISO 18033-2} section {@code 5.4.3}.
+         */
+        public static final int COMPRESSED = 1;
+    }
+
+    /**
+     * Default parameter spec: NIST P-256 curve (aka secp256r1 aka prime256v1), compressed point
+     * format, {@code HKDFwithSHA256}, DEM uses 128-bit AES GCM.
+     */
+    public static final EcIesParameterSpec DEFAULT = new EcIesParameterSpec(
+            "P-256",
+            PointFormat.COMPRESSED,
+            "HKDFwithSHA256",
+            "AES/GCM/NoPadding",
+            128,
+            null,
+            0);
+
+    private final String mKemCurveName;
+    private final @PointFormatEnum int mKemPointFormat;
+    private final String mKemKdfAlgorithm;
+    private final String mDemCipherTransformation;
+    private final int mDemCipherKeySize;
+    private final String mDemMacAlgorithm;
+    private final int mDemMacKeySize;
+
+    private EcIesParameterSpec(
+            String kemCurveName,
+            @PointFormatEnum int kemPointFormat,
+            String kemKdfAlgorithm,
+            String demCipherTransformation,
+            int demCipherKeySize,
+            String demMacAlgorithm,
+            int demMacKeySize) {
+        mKemCurveName = kemCurveName;
+        mKemPointFormat = kemPointFormat;
+        mKemKdfAlgorithm = kemKdfAlgorithm;
+        mDemCipherTransformation = demCipherTransformation;
+        mDemCipherKeySize = demCipherKeySize;
+        mDemMacAlgorithm = demMacAlgorithm;
+        mDemMacKeySize = demMacKeySize;
+    }
+
+    /**
+     * Returns KEM EC curve name (e.g., {@code secp256r1}) or {@code null} if not specified.
+     */
+    public String getKemCurveName() {
+        return mKemCurveName;
+    }
+
+    /**
+     * Returns KEM EC point wire format or {@link PointFormat#UNSPECIFIED} if not specified.
+     */
+    public @PointFormatEnum int getKemPointFormat() {
+        return mKemPointFormat;
+    }
+
+    /**
+     * Returns KEM KDF algorithm (e.g., {@code HKDFwithSHA256} or {@code KDF1withSHA1}) or
+     * {@code null} if not specified.
+     */
+    public String getKemKdfAlgorithm() {
+        return mKemKdfAlgorithm;
+    }
+
+    /**
+     * Returns DEM {@link Cipher} transformation (e.g., {@code AES/GCM/NoPadding} or
+     * {@code AES/CBC/PKCS7Padding}) or {@code null} if not specified.
+     *
+     * @see Cipher#getInstance(String)
+     * @see #getDemCipherKeySize()
+     */
+    public String getDemCipherTransformation() {
+        return mDemCipherTransformation;
+    }
+
+    /**
+     * Returns DEM {@link Cipher} key size in bits.
+     *
+     * @see #getDemCipherTransformation()
+     */
+    public int getDemCipherKeySize() {
+        return mDemCipherKeySize;
+    }
+
+    /**
+     * Returns DEM {@link Mac} algorithm (e.g., {@code HmacSHA256} or {@code HmacSHA1}) or
+     * {@code null} if not specified.
+     *
+     * @see Mac#getInstance(String)
+     * @see #getDemMacKeySize()
+     */
+    public String getDemMacAlgorithm() {
+        return mDemMacAlgorithm;
+    }
+
+    /**
+     * Returns DEM {@link Mac} key size in bits.
+     *
+     * @see #getDemCipherTransformation()
+     */
+    public int getDemMacKeySize() {
+        return mDemMacKeySize;
+    }
+
+    /**
+     * Builder of {@link EcIesParameterSpec}.
+     */
+    public static class Builder {
+        private String mKemCurveName;
+        private @PointFormatEnum int mKemPointFormat = PointFormat.UNSPECIFIED;
+        private String mKemKdfAlgorithm;
+        private String mDemCipherTransformation;
+        private int mDemCipherKeySize = 128;
+        private String mDemMacAlgorithm;
+        private int mDemMacKeySize = -1;
+
+        /**
+         * Sets KEM EC curve name. For example, {@code P-256} or {@code secp256r1}.
+         *
+         * <p>NOTE: Only curves with cofactor of {@code 1} are supported.
+         */
+        public Builder setKemCurveName(String name) {
+            mKemCurveName = name;
+            return this;
+        }
+
+        /**
+         * Sets KEM EC point wire format.
+         */
+        public Builder setKemPointFormat(@PointFormatEnum int pointFormat) {
+            mKemPointFormat = pointFormat;
+            return this;
+        }
+
+        /**
+         * Sets KEM KDF algorithm. For example, {@code HKDFwithSHA256}, {@code KDF2withSHA256}, or
+         * {@code KDF1withSHA1}.
+         */
+        public Builder setKemKdfAlgorithm(String algorithm) {
+            mKemKdfAlgorithm = algorithm;
+            return this;
+        }
+
+        /**
+         * Sets DEM {@link Cipher} transformation. For example, {@code AES/GCM/NoPadding},
+         * {@code AES/CBC/PKCS7Padding} or {@code AES/CTR/NoPadding}.
+         *
+         * @see Cipher#getInstance(String)
+         */
+        public Builder setDemCipherTransformation(String transformation) {
+            mDemCipherTransformation = transformation;
+            return this;
+        }
+
+        /**
+         * Returns DEM {@link Cipher} key size in bits.
+         *
+         * <p>The default value is {@code 128} bits.
+         *
+         * @see #setDemCipherTransformation(String)
+         */
+        public Builder setDemCipherKeySize(int sizeBits) {
+            mDemCipherKeySize = sizeBits;
+            return this;
+        }
+
+        /**
+         * Sets DEM {@link Mac} algorithm. For example, {@code HmacSHA256} or {@code HmacSHA1}.
+         *
+         * @see Mac#getInstance(String)
+         */
+        public Builder setDemMacAlgorithm(String algorithm) {
+            mDemMacAlgorithm = algorithm;
+            return this;
+        }
+
+        /**
+         * Sets DEM {@link Mac} key size in bits.
+         *
+         * <p>By default, {@code Mac} key size is the same as the {@code Cipher} key size.
+         *
+         * @see #setDemCipherKeySize(int)
+         */
+        public Builder setDemMacKeySize(int sizeBits) {
+            mDemMacKeySize = sizeBits;
+            return this;
+        }
+
+        /**
+         * Returns a new {@link EcIesParameterSpec} based on the current state of this builder.
+         */
+        public EcIesParameterSpec build() {
+            int demMacKeySize = (mDemMacKeySize != -1) ? mDemMacKeySize : mDemCipherKeySize;
+            return new EcIesParameterSpec(
+                    mKemCurveName,
+                    mKemPointFormat,
+                    mKemKdfAlgorithm,
+                    mDemCipherTransformation,
+                    mDemCipherKeySize,
+                    mDemMacAlgorithm,
+                    demMacKeySize
+                    );
+        }
+    }
+}
diff --git a/keystore/java/android/security/KeyStoreKeyCharacteristics.java b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
index 543b5d8..1f5d400 100644
--- a/keystore/java/android/security/KeyStoreKeyCharacteristics.java
+++ b/keystore/java/android/security/KeyStoreKeyCharacteristics.java
@@ -31,7 +31,7 @@
     private KeyStoreKeyCharacteristics() {}
 
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Origin.GENERATED_INSIDE_TEE, Origin.GENERATED_OUTSIDE_OF_TEE, Origin.IMPORTED})
+    @IntDef({Origin.GENERATED, Origin.IMPORTED})
     public @interface OriginEnum {}
 
     /**
@@ -40,14 +40,11 @@
     public static abstract class Origin {
         private Origin() {}
 
-        /** Key was generated inside a TEE. */
-        public static final int GENERATED_INSIDE_TEE = 1;
+        /** Key was generated inside AndroidKeyStore. */
+        public static final int GENERATED = 1 << 0;
 
-        /** Key was generated outside of a TEE. */
-        public static final int GENERATED_OUTSIDE_OF_TEE = 2;
-
-        /** Key was imported. */
-        public static final int IMPORTED = 0;
+        /** Key was imported into AndroidKeyStore. */
+        public static final int IMPORTED = 1 << 1;
 
         /**
          * @hide
@@ -55,9 +52,7 @@
         public static @OriginEnum int fromKeymaster(int origin) {
             switch (origin) {
                 case KeymasterDefs.KM_ORIGIN_HARDWARE:
-                    return GENERATED_INSIDE_TEE;
-                case KeymasterDefs.KM_ORIGIN_SOFTWARE:
-                    return GENERATED_OUTSIDE_OF_TEE;
+                    return GENERATED;
                 case KeymasterDefs.KM_ORIGIN_IMPORTED:
                     return IMPORTED;
                 default:
diff --git a/keystore/java/android/security/KeyStoreKeyConstraints.java b/keystore/java/android/security/KeyStoreKeyConstraints.java
index cde27f9..055b702 100644
--- a/keystore/java/android/security/KeyStoreKeyConstraints.java
+++ b/keystore/java/android/security/KeyStoreKeyConstraints.java
@@ -164,7 +164,7 @@
                 case RSA:
                     return KeymasterDefs.KM_ALGORITHM_RSA;
                 case EC:
-                    return KeymasterDefs.KM_ALGORITHM_ECDSA;
+                    return KeymasterDefs.KM_ALGORITHM_EC;
                 default:
                     throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
             }
@@ -181,7 +181,7 @@
                     return HMAC;
                 case KeymasterDefs.KM_ALGORITHM_RSA:
                     return RSA;
-                case KeymasterDefs.KM_ALGORITHM_ECDSA:
+                case KeymasterDefs.KM_ALGORITHM_EC:
                     return EC;
                 default:
                     throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
diff --git a/keystore/java/android/security/KeyStoreKeySpec.java b/keystore/java/android/security/KeyStoreKeySpec.java
index df4c958..27b444e 100644
--- a/keystore/java/android/security/KeyStoreKeySpec.java
+++ b/keystore/java/android/security/KeyStoreKeySpec.java
@@ -28,6 +28,7 @@
 public class KeyStoreKeySpec implements KeySpec {
     private final String mKeystoreAlias;
     private final int mKeySize;
+    private final boolean mTeeBacked;
     private final @KeyStoreKeyCharacteristics.OriginEnum int mOrigin;
     private final Date mKeyValidityStart;
     private final Date mKeyValidityForOriginationEnd;
@@ -46,6 +47,7 @@
      * @hide
      */
     KeyStoreKeySpec(String keystoreKeyAlias,
+            boolean teeBacked,
             @KeyStoreKeyCharacteristics.OriginEnum int origin,
             int keySize,
             Date keyValidityStart,
@@ -60,6 +62,7 @@
             @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators,
             int userAuthenticationValidityDurationSeconds) {
         mKeystoreAlias = keystoreKeyAlias;
+        mTeeBacked = teeBacked;
         mOrigin = origin;
         mKeySize = keySize;
         mKeyValidityStart = keyValidityStart;
@@ -83,6 +86,14 @@
     }
 
     /**
+     * Returns {@code true} if the key is TEE-backed. Key material of TEE-backed keys is available
+     * in plaintext only inside the TEE.
+     */
+    public boolean isTeeBacked() {
+        return mTeeBacked;
+    }
+
+    /**
      * Gets the origin of the key.
      */
     public @KeyStoreKeyCharacteristics.OriginEnum int getOrigin() {
diff --git a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
index 09f0b00..99a8168 100644
--- a/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/KeyStoreSecretKeyFactorySpi.java
@@ -70,7 +70,8 @@
                     + " Keystore error: " + errorCode);
         }
 
-        @KeyStoreKeyCharacteristics.OriginEnum Integer origin;
+        boolean teeBacked;
+        @KeyStoreKeyCharacteristics.OriginEnum int origin;
         int keySize;
         @KeyStoreKeyConstraints.PurposeEnum int purposes;
         @KeyStoreKeyConstraints.AlgorithmEnum int algorithm;
@@ -80,11 +81,17 @@
         @KeyStoreKeyConstraints.UserAuthenticatorEnum int userAuthenticators;
         @KeyStoreKeyConstraints.UserAuthenticatorEnum int teeEnforcedUserAuthenticators;
         try {
-            origin = KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_ORIGIN);
-            if (origin == null) {
+            if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
+                teeBacked = true;
+                origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(
+                        keyCharacteristics.hwEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
+            } else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
+                teeBacked = false;
+                origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(
+                        keyCharacteristics.swEnforced.getInt(KeymasterDefs.KM_TAG_ORIGIN, -1));
+            } else {
                 throw new InvalidKeySpecException("Key origin not available");
             }
-            origin = KeyStoreKeyCharacteristics.Origin.fromKeymaster(origin);
             Integer keySizeInteger =
                     KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_KEY_SIZE);
             if (keySizeInteger == null) {
@@ -144,6 +151,7 @@
                 KeymasterUtils.getInt(keyCharacteristics, KeymasterDefs.KM_TAG_AUTH_TIMEOUT);
 
         return new KeyStoreKeySpec(entryAlias,
+                teeBacked,
                 origin,
                 keySize,
                 keyValidityStart,
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index c9a140c..1a5552a 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -294,14 +294,14 @@
 
     public void testSaw_ungrantedUid_Bluetooth() throws Exception {
         String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
-        assertNull(results1);
+        assertEquals(0, results1.length);
 
         mKeyStore.password(TEST_PASSWD);
         mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
         mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, KeyStore.UID_SELF, KeyStore.FLAG_ENCRYPTED);
 
         String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
-        assertNull(results2);
+        assertEquals(0, results2.length);
     }
 
     public void testSaw_grantedUid_Wifi() throws Exception {
@@ -798,7 +798,7 @@
         // TODO: Verify we have an RSA public key that's well formed.
     }
 
-    public void testAesOcbEncryptSuccess() throws Exception {
+    public void testAesGcmEncryptSuccess() throws Exception {
         String name = "test";
         KeymasterArguments args = new KeymasterArguments();
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
@@ -806,7 +806,7 @@
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_GCM);
         args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
         args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
         args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
@@ -903,9 +903,7 @@
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
         args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
-        args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
-        args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_CTR);
         args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
@@ -935,11 +933,9 @@
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_ENCRYPT);
         args.addInt(KeymasterDefs.KM_TAG_PURPOSE, KeymasterDefs.KM_PURPOSE_DECRYPT);
         args.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_AES);
-        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_NONE);
+        args.addInt(KeymasterDefs.KM_TAG_PADDING, KeymasterDefs.KM_PAD_PKCS7);
         args.addInt(KeymasterDefs.KM_TAG_KEY_SIZE, 256);
-        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_OCB);
-        args.addInt(KeymasterDefs.KM_TAG_CHUNK_LENGTH, 4096);
-        args.addInt(KeymasterDefs.KM_TAG_MAC_LENGTH, 16);
+        args.addInt(KeymasterDefs.KM_TAG_BLOCK_MODE, KeymasterDefs.KM_MODE_ECB);
         args.addInt(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 1);
 
         KeyCharacteristics outCharacteristics = new KeyCharacteristics();
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 5b5b098..c1b50c1 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -183,6 +183,8 @@
     for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) {
         mRGBACacheTextures[i]->init();
     }
+
+    mDrawn = false;
 }
 
 void FontRenderer::flushLargeCaches(Vector<CacheTexture*>& cacheTextures) {
diff --git a/libs/hwui/RenderBufferCache.cpp b/libs/hwui/RenderBufferCache.cpp
index 830a13a..022820b 100644
--- a/libs/hwui/RenderBufferCache.cpp
+++ b/libs/hwui/RenderBufferCache.cpp
@@ -158,6 +158,11 @@
                 buffer->getWidth(), buffer->getHeight());
 
         return true;
+    } else {
+        RENDER_BUFFER_LOGD("Deleted %s render buffer (%dx%d) Size=%d, MaxSize=%d",
+        RenderBuffer::formatName(buffer->getFormat()),
+                 buffer->getWidth(), buffer->getHeight(), size, mMaxSize);
+        delete buffer;
     }
     return false;
 }
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 5138719..126b8c7 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -16,6 +16,7 @@
 
 package android.renderscript;
 
+import java.io.File;
 import java.lang.reflect.Method;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
@@ -231,6 +232,11 @@
         validate();
         rsnContextSetPriority(mContext, p);
     }
+    native void rsnContextSetCacheDir(long con, String cacheDir);
+    synchronized void nContextSetCacheDir(String cacheDir) {
+        validate();
+        rsnContextSetCacheDir(mContext, cacheDir);
+    }
     native void rsnContextDump(long con, int bits);
     synchronized void nContextDump(int bits) {
         validate();
@@ -1326,6 +1332,14 @@
         if (rs.mContext == 0) {
             throw new RSDriverException("Failed to create RS context.");
         }
+
+        // set up cache directory for entire context
+        final String CACHE_PATH = "com.android.renderscript.cache";
+        File f = new File(RenderScriptCacheDir.mCacheDir, CACHE_PATH);
+        String mCachePath = f.getAbsolutePath();
+        f.mkdirs();
+        rs.nContextSetCacheDir(mCachePath);
+
         rs.mMessageThread = new MessageThread(rs);
         rs.mMessageThread.start();
         return rs;
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 3591199..676d94f 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -689,6 +689,17 @@
     rsContextSetPriority((RsContext)con, p);
 }
 
+static void
+nContextSetCacheDir(JNIEnv *_env, jobject _this, jlong con, jstring cacheDir)
+{
+    AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
+
+    if (kLogApi) {
+        ALOGD("ContextSetCacheDir, con(%p), cacheDir(%s)", (RsContext)con, cacheDirUTF.c_str());
+    }
+    rsContextSetCacheDir((RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length());
+}
+
 
 
 static void
@@ -2312,6 +2323,7 @@
 {"rsnContextCreateGL",               "(JIIIIIIIIIIIIFI)J",                    (void*)nContextCreateGL },
 {"rsnContextFinish",                 "(J)V",                                  (void*)nContextFinish },
 {"rsnContextSetPriority",            "(JI)V",                                 (void*)nContextSetPriority },
+{"rsnContextSetCacheDir",            "(JLjava/lang/String;)V",                (void*)nContextSetCacheDir },
 {"rsnContextSetSurface",             "(JIILandroid/view/Surface;)V",          (void*)nContextSetSurface },
 {"rsnContextDestroy",                "(J)V",                                  (void*)nContextDestroy },
 {"rsnContextDump",                   "(JI)V",                                 (void*)nContextDump },
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 09ebe60..05a4d7e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -91,6 +91,7 @@
 import com.google.android.collect.Maps;
 
 import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -14163,7 +14164,7 @@
             } else if ("-h".equals(opt)) {
                 pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]");
                 pw.println("  -a: include all available information for each process.");
-                pw.println("  -d: include dalvik details when dumping process details.");
+                pw.println("  -d: include dalvik details.");
                 pw.println("  -c: dump in a compact machine-parseable representation.");
                 pw.println("  --oom: only show processes organized by oom adj.");
                 pw.println("  --local: only collect details locally, don't call process.");
@@ -14250,6 +14251,8 @@
         final SparseArray<MemItem> procMemsMap = new SparseArray<MemItem>();
         long nativePss = 0;
         long dalvikPss = 0;
+        long[] dalvikSubitemPss = dumpDalvik ? new long[Debug.MemoryInfo.NUM_DVK_STATS] :
+                EmptyArray.LONG;
         long otherPss = 0;
         long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS];
 
@@ -14327,6 +14330,9 @@
 
                     nativePss += mi.nativePss;
                     dalvikPss += mi.dalvikPss;
+                    for (int j=0; j<dalvikSubitemPss.length; j++) {
+                        dalvikSubitemPss[j] += mi.getOtherPss(Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                    }
                     otherPss += mi.otherPss;
                     for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                         long mem = mi.getOtherPss(j);
@@ -14385,6 +14391,10 @@
 
                         nativePss += mi.nativePss;
                         dalvikPss += mi.dalvikPss;
+                        for (int j=0; j<dalvikSubitemPss.length; j++) {
+                            dalvikSubitemPss[j] += mi.getOtherPss(
+                                    Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                        }
                         otherPss += mi.otherPss;
                         for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                             long mem = mi.getOtherPss(j);
@@ -14403,7 +14413,16 @@
             ArrayList<MemItem> catMems = new ArrayList<MemItem>();
 
             catMems.add(new MemItem("Native", "Native", nativePss, -1));
-            catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, -2));
+            final MemItem dalvikItem = new MemItem("Dalvik", "Dalvik", dalvikPss, -2);
+            if (dalvikSubitemPss.length > 0) {
+                dalvikItem.subitems = new ArrayList<MemItem>();
+                for (int j=0; j<dalvikSubitemPss.length; j++) {
+                    final String name = Debug.MemoryInfo.getOtherLabel(
+                            Debug.MemoryInfo.NUM_OTHER_STATS + j);
+                    dalvikItem.subitems.add(new MemItem(name, name, dalvikSubitemPss[j], j));
+                }
+            }
+            catMems.add(dalvikItem);
             catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3));
             for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) {
                 String label = Debug.MemoryInfo.getOtherLabel(j);
@@ -15690,8 +15709,9 @@
         }
 
         synchronized (this) {
-            if (callerApp != null && callerApp.pid == 0) {
-                // Caller already died
+            if (callerApp != null && (callerApp.thread == null
+                    || callerApp.thread.asBinder() != caller.asBinder())) {
+                // Original caller already died
                 return null;
             }
             ReceiverList rl
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 95ed7bc..d517642 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -37,6 +37,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -55,14 +56,15 @@
  */
 public final class SELinuxMMAC {
 
-    private static final String TAG = "SELinuxMMAC";
+    static final String TAG = "SELinuxMMAC";
 
     private static final boolean DEBUG_POLICY = false;
     private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
+    private static final boolean DEBUG_POLICY_ORDER = DEBUG_POLICY || false;
 
     // All policy stanzas read from mac_permissions.xml. This is also the lock
     // to synchronize access during policy load and access attempts.
-    private static final List<Policy> sPolicies = new ArrayList<Policy>();
+    private static List<Policy> sPolicies = new ArrayList<>();
 
     // Data policy override version file.
     private static final String DATA_VERSION_FILE =
@@ -115,17 +117,9 @@
      *         were loaded successfully; no partial loading is possible.
      */
     public static boolean readInstallPolicy() {
-        // Temp structure to hold the rules while we parse the xml file. We add
-        // all the rules once we know there's no problems.
+        // Temp structure to hold the rules while we parse the xml file
         List<Policy> policies = new ArrayList<>();
 
-        // A separate structure to hold the default stanza. We need to add this to
-        // the end of the policies list structure.
-        Policy defaultPolicy = null;
-
-        // Track sets of known policy certs so we can enforce rules across stanzas.
-        Set<Set<Signature>> knownCerts = new HashSet<>();
-
         FileReader policyFile = null;
         XmlPullParser parser = Xml.newPullParser();
         try {
@@ -141,31 +135,15 @@
                     continue;
                 }
 
-                String tagName = parser.getName();
-                if ("signer".equals(tagName)) {
-                    Policy signerPolicy = readSignerOrThrow(parser);
-                    // Return of a Policy instance ensures certain invariants have
-                    // passed, however, we still want to do some cross policy checking.
-                    // Thus, check that we haven't seen the certs in another stanza.
-                    Set<Signature> certs = signerPolicy.getSignatures();
-                    if (knownCerts.contains(certs)) {
-                        String msg = "Separate stanzas have identical certs";
-                        throw new IllegalStateException(msg);
-                    }
-                    knownCerts.add(certs);
-                    policies.add(signerPolicy);
-                } else if ("default".equals(tagName)) {
-                    Policy defPolicy = readDefaultOrThrow(parser);
-                    // Return of a Policy instance ensures certain invariants have
-                    // passed, however, we still want to do some cross policy checking.
-                    // Thus, check that we haven't already seen a default stanza.
-                    if (defaultPolicy != null) {
-                        String msg = "Multiple default stanzas identified";
-                        throw new IllegalStateException(msg);
-                    }
-                    defaultPolicy = defPolicy;
-                } else {
-                    skip(parser);
+                switch (parser.getName()) {
+                    case "signer":
+                        policies.add(readSignerOrThrow(parser));
+                        break;
+                    case "default":
+                        policies.add(readDefaultOrThrow(parser));
+                        break;
+                    default:
+                        skip(parser);
                 }
             }
         } catch (IllegalStateException | IllegalArgumentException |
@@ -185,15 +163,22 @@
             IoUtils.closeQuietly(policyFile);
         }
 
-        // Add the default policy to the end if there is one. This will ensure that
-        // the default stanza is consulted last when performing policy lookups.
-        if (defaultPolicy != null) {
-            policies.add(defaultPolicy);
+        // Now sort the policy stanzas
+        PolicyComparator policySort = new PolicyComparator();
+        Collections.sort(policies, policySort);
+        if (policySort.foundDuplicate()) {
+            Slog.w(TAG, "ERROR! Duplicate entries found parsing " + MAC_PERMISSIONS);
+            return false;
         }
 
         synchronized (sPolicies) {
-            sPolicies.clear();
-            sPolicies.addAll(policies);
+            sPolicies = policies;
+
+            if (DEBUG_POLICY_ORDER) {
+                for (Policy policy : sPolicies) {
+                    Slog.d(TAG, "Policy: " + policy.toString());
+                }
+            }
         }
 
         return true;
@@ -497,9 +482,23 @@
  * of invariants before being built and returned. Each instance can be guaranteed to
  * hold one valid policy stanza as outlined in the external/sepolicy/mac_permissions.xml
  * file.
- * </p>
+ * <p>
  * The following is an example of how to use {@link Policy.PolicyBuilder} to create a
- * signer based Policy instance.
+ * signer based Policy instance with only inner package name refinements.
+ * </p>
+ * <pre>
+ * {@code
+ * Policy policy = new Policy.PolicyBuilder()
+ *         .addSignature("308204a8...")
+ *         .addSignature("483538c8...")
+ *         .addInnerPackageMapOrThrow("com.foo.", "bar")
+ *         .addInnerPackageMapOrThrow("com.foo.other", "bar")
+ *         .build();
+ * }
+ * </pre>
+ * <p>
+ * The following is an example of how to use {@link Policy.PolicyBuilder} to create a
+ * signer based Policy instance with only a global seinfo tag.
  * </p>
  * <pre>
  * {@code
@@ -507,20 +506,18 @@
  *         .addSignature("308204a8...")
  *         .addSignature("483538c8...")
  *         .setGlobalSeinfoOrThrow("paltform")
- *         .addInnerPackageMapOrThrow("com.foo.", "bar")
- *         .addInnerPackageMapOrThrow("com.foo.other", "bar")
  *         .build();
  * }
  * </pre>
  * <p>
- * An example of how to use {@link Policy.PolicyBuilder} to create a default based Policy
- * instance.
+ * The following is an example of how to use {@link Policy.PolicyBuilder} to create a
+ * default based Policy instance.
  * </p>
  * <pre>
  * {@code
  * Policy policy = new Policy.PolicyBuilder()
  *         .setAsDefaultPolicy()
- *         .setGlobalSeinfoOrThrow("defualt")
+ *         .setGlobalSeinfoOrThrow("default")
  *         .build();
  * }
  * </pre>
@@ -551,6 +548,65 @@
     }
 
     /**
+     * Return whether this policy object represents a default stanza.
+     *
+     * @return A boolean indicating if this object represents a default policy stanza.
+     */
+    public boolean isDefaultStanza() {
+        return mDefaultStanza;
+    }
+
+    /**
+     * Return whether this policy object contains package name mapping refinements.
+     *
+     * @return A boolean indicating if this object has inner package name mappings.
+     */
+    public boolean hasInnerPackages() {
+        return !mPkgMap.isEmpty();
+    }
+
+    /**
+     * Return the mapping of all package name refinements.
+     *
+     * @return A Map object whose keys are the package names and whose values are
+     *         the seinfo assignments.
+     */
+    public Map<String, String> getInnerPackages() {
+        return mPkgMap;
+    }
+
+    /**
+     * Return whether the policy object has a global seinfo tag attached.
+     *
+     * @return A boolean indicating if this stanza has a global seinfo tag.
+     */
+    public boolean hasGlobalSeinfo() {
+        return mSeinfo != null;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if (mDefaultStanza) {
+            sb.append("defaultStanza=true ");
+        }
+
+        for (Signature cert : mCerts) {
+            sb.append("cert=" + cert.toCharsString().substring(0, 11) + "... ");
+        }
+
+        if (mSeinfo != null) {
+            sb.append("seinfo=" + mSeinfo);
+        }
+
+        for (String name : mPkgMap.keySet()) {
+            sb.append(" " + name + "=" + mPkgMap.get(name));
+        }
+
+        return sb.toString();
+    }
+
+    /**
      * <p>
      * Determine the seinfo value to assign to an apk. The appropriate seinfo value
      * is determined using the following steps:
@@ -623,7 +679,7 @@
         }
 
         /**
-         * Sets this stanza as a defualt stanza. All policy stanzas are assumed to
+         * Sets this stanza as a default stanza. All policy stanzas are assumed to
          * be signer stanzas unless this method is explicitly called. Default stanzas
          * are treated differently with respect to allowable child tags, ordering and
          * when and how policy decisions are enforced.
@@ -757,7 +813,7 @@
          *        <ul>
          *           <li> at least one cert must be found </li>
          *           <li> either a global seinfo value is present OR at least one
-         *           inner package mapping must be present. </li>
+         *           inner package mapping must be present BUT not both. </li>
          *        </ul>
          *      </li>
          *    </ul>
@@ -786,9 +842,9 @@
                     String err = "Missing certs with signer tag. Expecting at least one.";
                     throw new IllegalStateException(err);
                 }
-                if ((p.mSeinfo == null) && (p.mPkgMap.isEmpty())) {
-                    String err = "Missing seinfo OR package tags with signer tag. At " +
-                            "least one must be present.";
+                if (!(p.mSeinfo == null ^ p.mPkgMap.isEmpty())) {
+                    String err = "Only seinfo tag XOR package tags are allowed within " +
+                            "a signer stanza.";
                     throw new IllegalStateException(err);
                 }
             }
@@ -797,3 +853,58 @@
         }
     }
 }
+
+/**
+ * Comparision imposing an ordering on Policy objects. It is understood that Policy
+ * objects can only take one of three forms and ordered according to the following
+ * set of rules most specific to least.
+ * <ul>
+ *   <li> signer stanzas with inner package mappings </li>
+ *   <li> signer stanzas with global seinfo tags </li>
+ *   <li> default stanza </li>
+ * </ul>
+ * This comparison also checks for duplicate entries on the input selectors. Any
+ * found duplicates will be flagged and can be checked with {@link #foundDuplicate}.
+ */
+
+final class PolicyComparator implements Comparator<Policy> {
+
+    private boolean duplicateFound = false;
+
+    public boolean foundDuplicate() {
+        return duplicateFound;
+    }
+
+    @Override
+    public int compare(Policy p1, Policy p2) {
+
+        // Give precedence to signature stanzas over default stanzas
+        if (p1.isDefaultStanza() != p2.isDefaultStanza()) {
+            return p1.isDefaultStanza() ? 1 : -1;
+        }
+
+        // Give precedence to stanzas with inner package mappings
+        if (p1.hasInnerPackages() != p2.hasInnerPackages()) {
+            return p1.hasInnerPackages() ? -1 : 1;
+        }
+
+        // Check for duplicate entries
+        if (p1.getSignatures().equals(p2.getSignatures())) {
+            // Checks if default stanza or a signer w/o inner package names
+            if (p1.hasGlobalSeinfo()) {
+                duplicateFound = true;
+                Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString());
+            }
+
+            // Look for common inner package name mappings
+            final Map<String, String> p1Packages = p1.getInnerPackages();
+            final Map<String, String> p2Packages = p2.getInnerPackages();
+            if (!Collections.disjoint(p1Packages.keySet(), p2Packages.keySet())) {
+                duplicateFound = true;
+                Slog.e(SELinuxMMAC.TAG, "Duplicate policy entry: " + p1.toString());
+            }
+        }
+
+        return 0;
+    }
+}