Merge "Do not block intetn in a top level ClipData from being launched" into main
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index e271cf4..4e292d0 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -221,6 +221,12 @@
         // if the data is obtained from {@link #copyForTransferWithActivityInfo}
         private ActivityInfo mActivityInfo;
 
+        private boolean mTokenVerificationEnabled;
+
+        void setTokenVerificationEnabled() {
+            mTokenVerificationEnabled = true;
+        }
+
         /**
          * A builder for a ClipData Item.
          */
@@ -398,7 +404,9 @@
          * Retrieve the raw Intent contained in this Item.
          */
         public Intent getIntent() {
-            Intent.maybeMarkAsMissingCreatorToken(mIntent);
+            if (mTokenVerificationEnabled) {
+                Intent.maybeMarkAsMissingCreatorToken(mIntent);
+            }
             return mIntent;
         }
 
@@ -1353,6 +1361,12 @@
         }
     }
 
+    void setTokenVerificationEnabled() {
+        for (int i = 0; i < mItems.size(); ++i) {
+            mItems.get(i).setTokenVerificationEnabled();
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 350048d..01e24d81 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12506,6 +12506,9 @@
         if (intent.mExtras != null) {
             intent.mExtras.enableTokenVerification();
         }
+        if (intent.mClipData != null) {
+            intent.mClipData.setTokenVerificationEnabled();
+        }
     };
 
     /** @hide */
@@ -12517,6 +12520,9 @@
             // mark trusted creator token present.
             mExtras.enableTokenVerification();
         }
+        if (mClipData != null) {
+            mClipData.setTokenVerificationEnabled();
+        }
     }
 
     /** @hide */
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 6defadf..35ab2d2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -1454,6 +1454,18 @@
         assertThat(tokenForFullIntent.getKeyFields()).isEqualTo(tokenForCloneIntent.getKeyFields());
     }
 
+    @Test
+    public void testCanLaunchClipDataIntent() {
+        ClipData clipData = ClipData.newIntent("test", new Intent("test"));
+        clipData.prepareToLeaveProcess(true);
+        // skip mimicking sending clipData to another app because it will just be parceled and
+        // un-parceled.
+        Intent intent = clipData.getItemAt(0).getIntent();
+        // default intent redirect protection won't block an intent nested in a top level ClipData.
+        assertThat(intent.getExtendedFlags()
+                & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0);
+    }
+
     private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
             long lastNetworkUpdatedProcStateSeq,
             final long procStateSeqToWait, boolean expectWait) throws Exception {