Merge "Do not unparcel bundle from application in LaunchActivityItem" into rvc-dev
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 6d674ae..9ab6e7f 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -186,8 +186,8 @@
                 && Objects.equals(mOverrideConfig, other.mOverrideConfig)
                 && Objects.equals(mCompatInfo, other.mCompatInfo)
                 && Objects.equals(mReferrer, other.mReferrer)
-                && mProcState == other.mProcState && areBundlesEqual(mState, other.mState)
-                && areBundlesEqual(mPersistentState, other.mPersistentState)
+                && mProcState == other.mProcState && areBundlesEqualRoughly(mState, other.mState)
+                && areBundlesEqualRoughly(mPersistentState, other.mPersistentState)
                 && Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
                 && mIsForward == other.mIsForward
@@ -205,8 +205,8 @@
         result = 31 * result + Objects.hashCode(mCompatInfo);
         result = 31 * result + Objects.hashCode(mReferrer);
         result = 31 * result + Objects.hashCode(mProcState);
-        result = 31 * result + (mState != null ? mState.size() : 0);
-        result = 31 * result + (mPersistentState != null ? mPersistentState.size() : 0);
+        result = 31 * result + getRoughBundleHashCode(mState);
+        result = 31 * result + getRoughBundleHashCode(mPersistentState);
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
         result = 31 * result + (mIsForward ? 1 : 0);
@@ -225,25 +225,19 @@
                 && Objects.equals(mInfo.getComponentName(), other.getComponentName());
     }
 
-    private static boolean areBundlesEqual(BaseBundle extras, BaseBundle newExtras) {
-        if (extras == null || newExtras == null) {
-            return extras == newExtras;
-        }
+    /**
+     * This method may be used to compare a parceled item with another unparceled item, and the
+     * parceled bundle may contain customized class that will raise BadParcelableException when
+     * unparceling if a customized class loader is not set to the bundle. So the hash code is
+     * simply determined by the bundle is empty or not.
+     */
+    private static int getRoughBundleHashCode(BaseBundle bundle) {
+        return (bundle == null || bundle.isDefinitelyEmpty()) ? 0 : 1;
+    }
 
-        if (extras.size() != newExtras.size()) {
-            return false;
-        }
-
-        for (String key : extras.keySet()) {
-            if (key != null) {
-                final Object value = extras.get(key);
-                final Object newValue = newExtras.get(key);
-                if (!Objects.equals(value, newValue)) {
-                    return false;
-                }
-            }
-        }
-        return true;
+    /** Compares the bundles without unparceling them (avoid BadParcelableException). */
+    private static boolean areBundlesEqualRoughly(BaseBundle a, BaseBundle b) {
+        return getRoughBundleHashCode(a) == getRoughBundleHashCode(b);
     }
 
     @Override
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index f4fbefe..3766cf7 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -63,7 +63,6 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Map;
 
 /**
@@ -213,6 +212,7 @@
         int procState = 4;
         Bundle bundle = new Bundle();
         bundle.putString("key", "value");
+        bundle.putParcelable("data", new ParcelableData(1));
         PersistableBundle persistableBundle = new PersistableBundle();
         persistableBundle.putInt("k", 4);
 
@@ -374,6 +374,47 @@
         mParcel.setDataPosition(0);
     }
 
+    /**
+     * The parcelable class to make sure that when comparing the {@link LaunchActivityItem} or
+     * getting its hash code, the bundle is not unparceled. System shouldn't touch the data from
+     * application, otherwise it will cause exception as:
+     *   android.os.BadParcelableException: ClassNotFoundException when unmarshalling:
+     *   android.app.servertransaction.TransactionParcelTests$ParcelableData".
+     */
+    public static class ParcelableData implements Parcelable {
+        int mValue;
+
+        ParcelableData() {}
+
+        ParcelableData(int value) {
+            mValue = value;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(mValue);
+        }
+
+        public static final Creator<ParcelableData> CREATOR = new Creator<ParcelableData>() {
+            @Override
+            public ParcelableData createFromParcel(Parcel source) {
+                final ParcelableData data = new ParcelableData();
+                data.mValue = source.readInt();
+                return data;
+            }
+
+            @Override
+            public ParcelableData[] newArray(int size) {
+                return new ParcelableData[size];
+            }
+        };
+    }
+
     /** Stub implementation of IApplicationThread that can be presented as {@link Binder}. */
     class StubAppThread extends android.app.IApplicationThread.Stub  {