Merge "Add support for aapt:attr attributes"
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2664dc6..56f1e0c 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2745,15 +2745,15 @@
         String cls = clsSeq.toString();
         char c = cls.charAt(0);
         if (c == '.') {
-            return (pkg + cls).intern();
+            return pkg + cls;
         }
         if (cls.indexOf('.') < 0) {
             StringBuilder b = new StringBuilder(pkg);
             b.append('.');
             b.append(cls);
-            return b.toString().intern();
+            return b.toString();
         }
-        return cls.intern();
+        return cls;
     }
 
     private static String buildCompoundName(String pkg,
@@ -2773,7 +2773,7 @@
                         + pkg + ": " + nameError;
                 return null;
             }
-            return (pkg + proc).intern();
+            return pkg + proc;
         }
         String nameError = validateName(proc, true, false);
         if (nameError != null && !"system".equals(proc)) {
@@ -2781,7 +2781,7 @@
                     + pkg + ": " + nameError;
             return null;
         }
-        return proc.intern();
+        return proc;
     }
 
     private static String buildProcessName(String pkg, String defProc,
@@ -5083,7 +5083,7 @@
             if (v != null) {
                 if (v.type == TypedValue.TYPE_STRING) {
                     CharSequence cs = v.coerceToString();
-                    data.putString(name, cs != null ? cs.toString().intern() : null);
+                    data.putString(name, cs != null ? cs.toString() : null);
                 } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
                     data.putBoolean(name, v.data != 0);
                 } else if (v.type >= TypedValue.TYPE_FIRST_INT
@@ -5866,7 +5866,7 @@
             // We use the boot classloader for all classes that we load.
             final ClassLoader boot = Object.class.getClassLoader();
 
-            packageName = dest.readString();
+            packageName = dest.readString().intern();
             manifestPackageName = dest.readString();
             splitNames = dest.readStringArray();
             volumeUuid = dest.readString();
@@ -5879,6 +5879,9 @@
             splitPrivateFlags = dest.createIntArray();
             baseHardwareAccelerated = (dest.readInt() == 1);
             applicationInfo = dest.readParcelable(boot);
+            if (applicationInfo.permission != null) {
+                applicationInfo.permission = applicationInfo.permission.intern();
+            }
 
             // We don't serialize the "owner" package and the application info object for each of
             // these components, in order to save space and to avoid circular dependencies while
@@ -5899,7 +5902,10 @@
             fixupOwner(instrumentation);
 
             dest.readStringList(requestedPermissions);
+            internStringArrayList(requestedPermissions);
             protectedBroadcasts = dest.createStringArrayList();
+            internStringArrayList(protectedBroadcasts);
+
             parentPackage = dest.readParcelable(boot);
 
             childPackages = new ArrayList<>();
@@ -5909,16 +5915,23 @@
             }
 
             staticSharedLibName = dest.readString();
+            if (staticSharedLibName != null) {
+                staticSharedLibName = staticSharedLibName.intern();
+            }
             staticSharedLibVersion = dest.readInt();
             libraryNames = dest.createStringArrayList();
+            internStringArrayList(libraryNames);
             usesLibraries = dest.createStringArrayList();
+            internStringArrayList(usesLibraries);
             usesOptionalLibraries = dest.createStringArrayList();
+            internStringArrayList(usesOptionalLibraries);
             usesLibraryFiles = dest.readStringArray();
 
             final int libCount = dest.readInt();
             if (libCount > 0) {
                 usesStaticLibraries = new ArrayList<>(libCount);
                 dest.readStringList(usesStaticLibraries);
+                internStringArrayList(usesStaticLibraries);
                 usesStaticLibrariesVersions = new int[libCount];
                 dest.readIntArray(usesStaticLibrariesVersions);
                 usesStaticLibrariesCertDigests = new String[libCount];
@@ -5937,7 +5950,13 @@
             mAppMetaData = dest.readBundle();
             mVersionCode = dest.readInt();
             mVersionName = dest.readString();
+            if (mVersionName != null) {
+                mVersionName = mVersionName.intern();
+            }
             mSharedUserId = dest.readString();
+            if (mSharedUserId != null) {
+                mSharedUserId = mSharedUserId.intern();
+            }
             mSharedUserLabel = dest.readInt();
 
             mSignatures = (Signature[]) dest.readParcelableArray(boot, Signature.class);
@@ -5988,6 +6007,15 @@
             restrictUpdateHash = dest.createByteArray();
         }
 
+        private static void internStringArrayList(List<String> list) {
+            if (list != null) {
+                final int N = list.size();
+                for (int i = 0; i < N; ++i) {
+                    list.set(i, list.get(i).intern());
+                }
+            }
+        }
+
         /**
          * Sets the package owner and the the {@code applicationInfo} for every component
          * owner by this package.
@@ -6375,6 +6403,10 @@
             super(in);
             final ClassLoader boot = Object.class.getClassLoader();
             info = in.readParcelable(boot);
+            if (info.group != null) {
+                info.group = info.group.intern();
+            }
+
             tree = (in.readInt() == 1);
             group = in.readParcelable(boot);
         }
@@ -6651,6 +6683,10 @@
             for (ActivityIntentInfo aii : intents) {
                 aii.activity = this;
             }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Activity>() {
@@ -6735,6 +6771,10 @@
             for (ServiceIntentInfo aii : intents) {
                 aii.service = this;
             }
+
+            if (info.permission != null) {
+                info.permission = info.permission.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Service>() {
@@ -6816,6 +6856,18 @@
             for (ProviderIntentInfo aii : intents) {
                 aii.provider = this;
             }
+
+            if (info.readPermission != null) {
+                info.readPermission = info.readPermission.intern();
+            }
+
+            if (info.writePermission != null) {
+                info.writePermission = info.writePermission.intern();
+            }
+
+            if (info.authority != null) {
+                info.authority = info.authority.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Provider>() {
@@ -6888,6 +6940,14 @@
         private Instrumentation(Parcel in) {
             super(in);
             info = in.readParcelable(Object.class.getClassLoader());
+
+            if (info.targetPackage != null) {
+                info.targetPackage = info.targetPackage.intern();
+            }
+
+            if (info.targetProcess != null) {
+                info.targetProcess = info.targetProcess.intern();
+            }
         }
 
         public static final Parcelable.Creator CREATOR = new Parcelable.Creator<Instrumentation>() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 5591029..384f49f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -140,6 +140,36 @@
         assertAllFieldsExist(deserialized);
     }
 
+    @Test
+    public void test_stringInterning() throws Exception {
+        PackageParser.Package pkg = new PackageParser.Package("foo");
+        setKnownFields(pkg);
+
+        Parcel p = Parcel.obtain();
+        pkg.writeToParcel(p, 0 /* flags */);
+
+        p.setDataPosition(0);
+        PackageParser.Package deserialized = new PackageParser.Package(p);
+
+        p.setDataPosition(0);
+        PackageParser.Package deserialized2 = new PackageParser.Package(p);
+
+        assertSame(deserialized.packageName, deserialized2.packageName);
+        assertSame(deserialized.applicationInfo.permission,
+                deserialized2.applicationInfo.permission);
+        assertSame(deserialized.requestedPermissions.get(0),
+                deserialized2.requestedPermissions.get(0));
+        assertSame(deserialized.protectedBroadcasts.get(0),
+                deserialized2.protectedBroadcasts.get(0));
+        assertSame(deserialized.usesLibraries.get(0),
+                deserialized2.usesLibraries.get(0));
+        assertSame(deserialized.usesOptionalLibraries.get(0),
+                deserialized2.usesOptionalLibraries.get(0));
+        assertSame(deserialized.mVersionName, deserialized2.mVersionName);
+        assertSame(deserialized.mSharedUserId, deserialized2.mSharedUserId);
+    }
+
+
     /**
      * A trivial subclass of package parser that only caches the package name, and throws away
      * all other information.
@@ -407,11 +437,11 @@
         pkg.mTrustedOverlay = true;
         pkg.use32bitAbi = true;
         pkg.packageName = "foo";
-        pkg.splitNames = new String[] { "foo" };
-        pkg.volumeUuid = "foo";
-        pkg.codePath = "foo";
-        pkg.baseCodePath = "foo";
-        pkg.splitCodePaths = new String[] { "foo" };
+        pkg.splitNames = new String[] { "foo2" };
+        pkg.volumeUuid = "foo3";
+        pkg.codePath = "foo4";
+        pkg.baseCodePath = "foo5";
+        pkg.splitCodePaths = new String[] { "foo6" };
         pkg.splitRevisionCodes = new int[] { 100 };
         pkg.splitFlags = new int[] { 100 };
         pkg.splitPrivateFlags = new int[] { 100 };
@@ -428,48 +458,55 @@
         pkg.providers.add(new PackageParser.Provider(dummy, new ProviderInfo()));
         pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
         pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
-        pkg.requestedPermissions.add("foo");
+        pkg.requestedPermissions.add("foo7");
 
         pkg.protectedBroadcasts = new ArrayList<>();
-        pkg.protectedBroadcasts.add("foo");
+        pkg.protectedBroadcasts.add("foo8");
 
-        pkg.parentPackage = new PackageParser.Package("foo");
+        pkg.parentPackage = new PackageParser.Package("foo9");
 
         pkg.childPackages = new ArrayList<>();
         pkg.childPackages.add(new PackageParser.Package("bar"));
 
+        pkg.staticSharedLibName = "foo23";
+        pkg.staticSharedLibVersion = 100;
+        pkg.usesStaticLibraries = new ArrayList<>();
+        pkg.usesStaticLibraries.add("foo23");
+        pkg.usesStaticLibrariesCertDigests = new String[] { "digest" };
+        pkg.usesStaticLibrariesVersions = new int[] { 100 };
+
         pkg.libraryNames = new ArrayList<>();
-        pkg.libraryNames.add("foo");
+        pkg.libraryNames.add("foo10");
 
         pkg.usesLibraries = new ArrayList<>();
-        pkg.usesLibraries.add("foo");
+        pkg.usesLibraries.add("foo11");
 
         pkg.usesOptionalLibraries = new ArrayList<>();
-        pkg.usesOptionalLibraries.add("foo");
+        pkg.usesOptionalLibraries.add("foo12");
 
-        pkg.usesLibraryFiles = new String[] { "foo "};
+        pkg.usesLibraryFiles = new String[] { "foo13"};
 
         pkg.mOriginalPackages = new ArrayList<>();
-        pkg.mOriginalPackages.add("foo");
+        pkg.mOriginalPackages.add("foo14");
 
-        pkg.mRealPackage = "foo";
+        pkg.mRealPackage = "foo15";
 
         pkg.mAdoptPermissions = new ArrayList<>();
-        pkg.mAdoptPermissions.add("foo");
+        pkg.mAdoptPermissions.add("foo16");
 
         pkg.mAppMetaData = new Bundle();
-        pkg.mVersionName = "foo";
-        pkg.mSharedUserId = "foo";
+        pkg.mVersionName = "foo17";
+        pkg.mSharedUserId = "foo18";
         pkg.mSignatures = new Signature[] { new Signature(new byte[16]) };
         pkg.mCertificates = new Certificate[][] { new Certificate[] { null }};
         pkg.mExtras = new Bundle();
-        pkg.mRestrictedAccountType = "foo";
-        pkg.mRequiredAccountType = "foo";
-        pkg.mOverlayTarget = "foo";
+        pkg.mRestrictedAccountType = "foo19";
+        pkg.mRequiredAccountType = "foo20";
+        pkg.mOverlayTarget = "foo21";
         pkg.mSigningKeys = new ArraySet<>();
         pkg.mUpgradeKeySets = new ArraySet<>();
         pkg.mKeySetMapping = new ArrayMap<>();
-        pkg.cpuAbiOverride = "foo";
+        pkg.cpuAbiOverride = "foo22";
         pkg.restrictUpdateHash = new byte[16];
 
         pkg.preferredActivityFilters = new ArrayList<>();
@@ -504,7 +541,7 @@
                 // Sanity check for list fields: Assume they're non-null and contain precisely
                 // one element.
                 List<?> list = (List<?>) f.get(pkg);
-                assertNotNull(list);
+                assertNotNull("List was null: " + f, list);
                 assertEquals(1, list.size());
             } else if (fieldType.getComponentType() != null) {
                 // Sanity check for array fields: Assume they're non-null and contain precisely
@@ -514,15 +551,16 @@
             } else if (fieldType == String.class) {
                 // String fields: Check that they're set to "foo".
                 String value = (String) f.get(pkg);
-                assertEquals("foo", value);
+
+                assertTrue("Bad value for field: " + f, value != null && value.startsWith("foo"));
             } else if (fieldType == int.class) {
                 // int fields: Check that they're set to 100.
                 int value = (int) f.get(pkg);
-                assertEquals(100, value);
+                assertEquals("Bad value for field: " + f, 100, value);
             } else {
                 // All other fields: Check that they're set.
                 Object o = f.get(pkg);
-                assertNotNull("Field was null: " + f.getName(), o);
+                assertNotNull("Field was null: " + f, o);
             }
         }
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 2c9fe29..3d5d5c6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -65,6 +65,7 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteDatabase.CursorFactory;
 import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.net.Uri;
@@ -381,6 +382,18 @@
             return true;
         }
 
+        String stringValue = value.getValue();
+        if (!stringValue.isEmpty()) {
+            if (stringValue.charAt(0) == '#') {
+                outValue.type = TypedValue.TYPE_INT_COLOR_ARGB8;
+                outValue.data = Color.parseColor(value.getValue());
+            }
+            else if (stringValue.charAt(0) == '@') {
+                outValue.type = TypedValue.TYPE_REFERENCE;
+            }
+
+        }
+
         int a;
         // if this is a framework value.
         if (value.isFramework()) {
@@ -399,7 +412,7 @@
         }
 
         // If the value is not a valid reference, fallback to pass the value as a string.
-        outValue.string = value.getValue();
+        outValue.string = stringValue;
         return true;
     }
 
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 913519c..9e60f0f 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -36,6 +36,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.util.DisplayMetrics;
+import android.util.TypedValue;
 
 import java.lang.reflect.Field;
 import java.util.concurrent.TimeUnit;
@@ -43,6 +44,7 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -334,6 +336,7 @@
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
         Resources resources = new Resources(assetManager, metrics, configuration);
         resources.mLayoutlibCallback = params.getLayoutlibCallback();
         resources.mContext =
@@ -370,6 +373,7 @@
         AssetManager assetManager = AssetManager.getSystem();
         DisplayMetrics metrics = new DisplayMetrics();
         Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
         Resources resources = new Resources(assetManager, metrics, configuration);
         resources.mLayoutlibCallback = params.getLayoutlibCallback();
         resources.mContext =
@@ -390,4 +394,34 @@
         // TODO: styles seem to be broken in TextView
         renderAndVerify("fonts_test.xml", "font_test.png");
     }
+
+    @Test
+    public void testColorTypedValue() throws Exception {
+        // Setup
+        // Create the layout pull parser for our resources (empty.xml can not be part of the test
+        // app as it won't compile).
+        LayoutPullParser parser = new LayoutPullParser("/empty.xml");
+        // Create LayoutLibCallback.
+        LayoutLibTestCallback layoutLibCallback =
+                new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
+        layoutLibCallback.initResources();
+        SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_4,
+                layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22);
+        AssetManager assetManager = AssetManager.getSystem();
+        DisplayMetrics metrics = new DisplayMetrics();
+        Configuration configuration = RenderAction.getConfiguration(params);
+        //noinspection deprecation
+        Resources resources = new Resources(assetManager, metrics, configuration);
+        resources.mLayoutlibCallback = params.getLayoutlibCallback();
+        resources.mContext =
+                new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+                        params.getAssets(), params.getLayoutlibCallback(), configuration,
+                        params.getTargetSdkVersion(), params.isRtlSupported());
+
+        TypedValue outValue = new TypedValue();
+        resources.mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
+        assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
+        assertNotEquals(0, outValue.data);
+        assertTrue(sRenderMessages.isEmpty());
+    }
 }