Avoid parceling redundant ApplicationInfo objects within PackageInfo

Two benefits:

  1) marshaling one flattened ApplicationInfo as part of a PackageInfo parcel
     rather than one per included ComponentInfo; and

  2) producing one ApplicationInfo at unmarshaling time and sharing the
     reference to it among all included ComponentInfo instances, rather
     than the previous implementation that generated a separate
     ApplicationInfo instance for each ComponentInfo.

In some cases there can be many hundreds of ComponentInfo objects embedded
in a single PackageInfo, so coalescing duplicates is a significant win
for both payload size and object pressure.

Bug 19519502
Bug 20453802

Change-Id: Ib888810dad4471084fab9ead1ebb5e0b932905f1
diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java
index cc06b67..f27fc2a 100644
--- a/core/java/android/content/pm/ComponentInfo.java
+++ b/core/java/android/content/pm/ComponentInfo.java
@@ -18,6 +18,7 @@
 
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Printer;
 
 /**
@@ -160,7 +161,12 @@
     
     public void writeToParcel(Parcel dest, int parcelableFlags) {
         super.writeToParcel(dest, parcelableFlags);
-        applicationInfo.writeToParcel(dest, parcelableFlags);
+        if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            applicationInfo.writeToParcel(dest, parcelableFlags);
+        }
         dest.writeString(processName);
         dest.writeInt(descriptionRes);
         dest.writeInt(enabled ? 1 : 0);
@@ -169,7 +175,10 @@
     
     protected ComponentInfo(Parcel source) {
         super(source);
-        applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+        final boolean hasApplicationInfo = (source.readInt() != 0);
+        if (hasApplicationInfo) {
+            applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source);
+        }
         processName = source.readString();
         descriptionRes = source.readInt();
         enabled = (source.readInt() != 0);
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 9e6c6b5..d40bab5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -305,10 +305,10 @@
         dest.writeLong(firstInstallTime);
         dest.writeLong(lastUpdateTime);
         dest.writeIntArray(gids);
-        dest.writeTypedArray(activities, parcelableFlags);
-        dest.writeTypedArray(receivers, parcelableFlags);
-        dest.writeTypedArray(services, parcelableFlags);
-        dest.writeTypedArray(providers, parcelableFlags);
+        dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+        dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+        dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
+        dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES);
         dest.writeTypedArray(instrumentation, parcelableFlags);
         dest.writeTypedArray(permissions, parcelableFlags);
         dest.writeStringArray(requestedPermissions);
@@ -372,5 +372,22 @@
         restrictedAccountType = source.readString();
         requiredAccountType = source.readString();
         overlayTarget = source.readString();
+
+        // The component lists were flattened with the redundant ApplicationInfo
+        // instances omitted.  Distribute the canonical one here as appropriate.
+        if (applicationInfo != null) {
+            propagateApplicationInfo(applicationInfo, activities);
+            propagateApplicationInfo(applicationInfo, receivers);
+            propagateApplicationInfo(applicationInfo, services);
+            propagateApplicationInfo(applicationInfo, providers);
+        }
+    }
+
+    private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) {
+        if (components != null) {
+            for (ComponentInfo ci : components) {
+                ci.applicationInfo = appInfo;
+            }
+        }
     }
 }
diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java
index 448b591..13b5de9 100644
--- a/core/java/android/os/Parcelable.java
+++ b/core/java/android/os/Parcelable.java
@@ -62,7 +62,17 @@
      * may want to release resources at this point.
      */
     public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001;
-    
+
+    /**
+     * Flag for use with {@link #writeToParcel}: a parent object will take
+     * care of managing duplicate state/data that is nominally replicated
+     * across its inner data members.  This flag instructs the inner data
+     * types to omit that data during marshaling.  Exact behavior may vary
+     * on a case by case basis.
+     * @hide
+     */
+    public static final int PARCELABLE_ELIDE_DUPLICATES = 0x0002;
+
     /**
      * Bit masks for use with {@link #describeContents}: each bit represents a
      * kind of object considered to have potential special significance when