Loading core/java/android/app/IActivityManager.aidl +10 −0 Original line number Diff line number Diff line Loading @@ -1028,4 +1028,14 @@ interface IActivityManager { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DEVICE_POWER)") void noteAppRestrictionEnabled(in String packageName, int uid, int restrictionType, boolean enabled, int reason, in String subReason, int source, long threshold); /** * Creates and returns a new IntentCreatorToken that keeps the creatorUid and refreshes key * fields of the intent passed in. * * @param intent The intent with key fields out of sync of the IntentCreatorToken it contains. * @hide */ @EnforcePermission("INTERACT_ACROSS_USERS_FULL") IBinder refreshIntentCreatorToken(in Intent intent); } core/java/android/content/ClipData.java +28 −0 Original line number Diff line number Diff line Loading @@ -945,6 +945,34 @@ public class ClipData implements Parcelable { return mParcelItemActivityInfos; } /** * Make a clone of ClipData that only contains URIs. This reduces the size of data transfer over * IPC and only retains important information for the purpose of verifying creator token of an * Intent. * @return a copy of ClipData with only URIs remained. * @hide */ public ClipData cloneOnlyUriItems() { ArrayList<Item> items = null; final int N = mItems.size(); for (int i = 0; i < N; i++) { Item item = mItems.get(i); if (item.getUri() != null) { if (items == null) { items = new ArrayList<>(N); } items.add(new Item(item.getUri())); } else if (item.getIntent() != null) { if (items == null) { items = new ArrayList<>(N); } items.add(new Item(item.getIntent().cloneForCreatorToken())); } } if (items == null || items.isEmpty()) return null; return new ClipData(new ClipDescription("", new String[0]), items); } /** * Create a new ClipData holding data of the type * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}. Loading core/java/android/content/Intent.java +59 −1 Original line number Diff line number Diff line Loading @@ -7969,6 +7969,24 @@ public class Intent implements Parcelable, Cloneable { return new Intent(this, COPY_MODE_FILTER); } /** * Make a copy of all members important to identify an intent with its creator token. * @hide */ public @NonNull Intent cloneForCreatorToken() { Intent clone = new Intent() .setAction(this.mAction) .setDataAndType(this.mData, this.mType) .setPackage(this.mPackage) .setComponent(this.mComponent) .setFlags(this.mFlags & IMMUTABLE_FLAGS); if (this.mClipData != null) { clone.setClipData(this.mClipData.cloneOnlyUriItems()); } clone.mCreatorTokenInfo = this.mCreatorTokenInfo; return clone; } /** * Create an intent with a given action. All other fields (data, type, * class) are null. Note that the action <em>must</em> be in a Loading Loading @@ -11684,7 +11702,7 @@ public class Intent implements Parcelable, Cloneable { Log.w(TAG, "Failure filling in extras", e); } } mCreatorTokenInfo = other.mCreatorTokenInfo; fillInCreatorTokenInfo(other.mCreatorTokenInfo, changes); if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT && other.mContentUserHint != UserHandle.USER_CURRENT) { mContentUserHint = other.mContentUserHint; Loading @@ -11692,6 +11710,45 @@ public class Intent implements Parcelable, Cloneable { return changes; } // keep original creator token and merge nested intent keys. private void fillInCreatorTokenInfo(CreatorTokenInfo otherCreatorTokenInfo, int changes) { if (otherCreatorTokenInfo != null && otherCreatorTokenInfo.mNestedIntentKeys != null) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } ArraySet<NestedIntentKey> otherNestedIntentKeys = otherCreatorTokenInfo.mNestedIntentKeys; if (mCreatorTokenInfo.mNestedIntentKeys == null) { mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(otherNestedIntentKeys); } else { ArraySet<NestedIntentKey> otherKeys; if ((changes & FILL_IN_CLIP_DATA) == 0) { // If clip data is Not filled in from other, do not merge clip data keys. otherKeys = new ArraySet<>(); int N = otherNestedIntentKeys.size(); for (int i = 0; i < N; i++) { NestedIntentKey key = otherNestedIntentKeys.valueAt(i); if (key.mType != NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) { otherKeys.add(key); } } } else { // If clip data is filled in from other, remove clip data keys from this // creatorTokenInfo and then merge every key from the others. int N = mCreatorTokenInfo.mNestedIntentKeys.size(); for (int i = N - 1; i >= 0; i--) { NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i); if (key.mType == NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) { mCreatorTokenInfo.mNestedIntentKeys.removeAt(i); } } otherKeys = otherNestedIntentKeys; } mCreatorTokenInfo.mNestedIntentKeys.addAll(otherKeys); } } } /** * Merge the extras data in this intent with that of other supplied intent using the * strategy specified using {@code extrasMerger}. Loading Loading @@ -12228,6 +12285,7 @@ public class Intent implements Parcelable, Cloneable { private IBinder mCreatorToken; // Stores all extra keys whose values are intents for a top level intent. private ArraySet<NestedIntentKey> mNestedIntentKeys; } /** Loading core/tests/coretests/src/android/content/IntentTest.java +111 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,9 @@ package android.content; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; Loading @@ -29,6 +30,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.security.Flags; import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; Loading @@ -40,6 +42,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; /** * Build/Install/Run: Loading @@ -51,7 +54,6 @@ import java.util.List; public class IntentTest { private static final String TEST_ACTION = "android.content.IntentTest_test"; private static final String TEST_EXTRA_NAME = "testExtraName"; private static final Uri TEST_URI = Uri.parse("content://com.example/people"); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); Loading Loading @@ -129,4 +131,111 @@ public class IntentTest { } } @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testFillInCreatorTokenInfo() { // case 1: intent does not have creatorTokenInfo; fillinIntent contains creatorTokenInfo Intent intent = new Intent(); Intent fillInIntent = new Intent(); fillInIntent.setCreatorToken(new Binder()); fillInIntent.putExtra("extraKey", new Intent()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); // extra intent keys are merged assertThat(intent.getExtraIntentKeys()).isEqualTo(fillInIntent.getExtraIntentKeys()); // but creator token is not overwritten. assertThat(intent.getCreatorToken()).isNull(); // case 2: Both intent and fillInIntent contains creatorToken, intent's creatorToken is not // overwritten. intent = new Intent(); IBinder creatorToken = new Binder(); intent.setCreatorToken(creatorToken); fillInIntent = new Intent(); fillInIntent.setCreatorToken(new Binder()); intent.fillIn(fillInIntent, 0); assertThat(intent.getCreatorToken()).isEqualTo(creatorToken); // case 3: Contains duplicate extra keys intent = new Intent(); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); Set originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); assertThat(intent.getExtraIntentKeys()).hasSize(3); assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // case 4: Both contains a mixture of extras and clip data. NOT force to fill in clip data. intent = new Intent(); ClipData clipData = ClipData.newIntent("clip", new Intent()); clipData.addItem(new ClipData.Item(new Intent())); intent.setClipData(clipData); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); ClipData fillInClipData = ClipData.newIntent("clip", new Intent()); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInIntent.setClipData(fillInClipData); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); // size is 5 ( 3 extras merged from both + 2 clip data in the original. assertThat(intent.getExtraIntentKeys()).hasSize(5); // all keys from original are kept. assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); // Not all keys from fillInIntent are kept - clip data keys are dropped. assertFalse(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // case 5: Both contains a mixture of extras and clip data. Force to fill in clip data. intent = new Intent(); clipData = ClipData.newIntent("clip", new Intent()); clipData.addItem(new ClipData.Item(new Intent())); clipData.addItem(new ClipData.Item(new Intent())); clipData.addItem(new ClipData.Item(new Intent())); intent.setClipData(clipData); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); fillInClipData = ClipData.newIntent("clip", new Intent()); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInIntent.setClipData(fillInClipData); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, Intent.FILL_IN_CLIP_DATA); // size is 6 ( 3 extras merged from both + 3 clip data in the fillInIntent. assertThat(intent.getExtraIntentKeys()).hasSize(6); // all keys from fillInIntent are kept. assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // Not all keys from intent are kept - clip data keys are dropped. assertFalse(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); } } services/core/java/com/android/server/am/ActivityManagerService.java +37 −4 Original line number Diff line number Diff line Loading @@ -192,6 +192,7 @@ import static com.android.systemui.shared.Flags.enableHomeDelay; import android.Manifest; import android.Manifest.permission; import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.PermissionMethod; Loading Loading @@ -19228,6 +19229,11 @@ public class ActivityManagerService extends IActivityManager.Stub return mKeyFields.mCreatorPackage; } @VisibleForTesting public @NonNull Key getKeyFields() { return mKeyFields; } public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; Loading Loading @@ -19271,9 +19277,13 @@ public class ActivityManagerService extends IActivityManager.Stub this.mFlags = intent.getFlags() & Intent.IMMUTABLE_FLAGS; ClipData clipData = intent.getClipData(); if (clipData != null) { this.mClipDataUris = new ArrayList<>(clipData.getItemCount()); for (int i = 0; i < clipData.getItemCount(); i++) { this.mClipDataUris.add(clipData.getItemAt(i).getUri()); clipData = clipData.cloneOnlyUriItems(); if (clipData != null) { List<Uri> clipDataUris = new ArrayList<>(); clipData.collectUris(clipDataUris); if (!clipDataUris.isEmpty()) { this.mClipDataUris = clipDataUris; } } } } Loading Loading @@ -19375,11 +19385,34 @@ public class ActivityManagerService extends IActivityManager.Stub String creatorPackage) { if (IntentCreatorToken.isValid(intent)) return null; IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent); return createOrGetIntentCreatorToken(intent, key); } /** * @hide */ @EnforcePermission("android.permission.INTERACT_ACROSS_USERS_FULL") public IBinder refreshIntentCreatorToken(Intent intent) { refreshIntentCreatorToken_enforcePermission(); IBinder binder = intent.getCreatorToken(); if (binder instanceof IntentCreatorToken) { IntentCreatorToken token = (IntentCreatorToken) binder; IntentCreatorToken.Key key = new IntentCreatorToken.Key(token.getCreatorUid(), token.getCreatorPackage(), intent); return createOrGetIntentCreatorToken(intent, key); } else { throw new IllegalArgumentException("intent does not contain a creator token."); } } private static IntentCreatorToken createOrGetIntentCreatorToken(Intent intent, IntentCreatorToken.Key key) { IntentCreatorToken token; synchronized (sIntentCreatorTokenCache) { WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key); if (ref == null || ref.get() == null) { token = new IntentCreatorToken(creatorUid, creatorPackage, intent); token = new IntentCreatorToken(key.mCreatorUid, key.mCreatorPackage, intent); sIntentCreatorTokenCache.put(key, token.mRef); } else { token = ref.get(); Loading
core/java/android/app/IActivityManager.aidl +10 −0 Original line number Diff line number Diff line Loading @@ -1028,4 +1028,14 @@ interface IActivityManager { @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.DEVICE_POWER)") void noteAppRestrictionEnabled(in String packageName, int uid, int restrictionType, boolean enabled, int reason, in String subReason, int source, long threshold); /** * Creates and returns a new IntentCreatorToken that keeps the creatorUid and refreshes key * fields of the intent passed in. * * @param intent The intent with key fields out of sync of the IntentCreatorToken it contains. * @hide */ @EnforcePermission("INTERACT_ACROSS_USERS_FULL") IBinder refreshIntentCreatorToken(in Intent intent); }
core/java/android/content/ClipData.java +28 −0 Original line number Diff line number Diff line Loading @@ -945,6 +945,34 @@ public class ClipData implements Parcelable { return mParcelItemActivityInfos; } /** * Make a clone of ClipData that only contains URIs. This reduces the size of data transfer over * IPC and only retains important information for the purpose of verifying creator token of an * Intent. * @return a copy of ClipData with only URIs remained. * @hide */ public ClipData cloneOnlyUriItems() { ArrayList<Item> items = null; final int N = mItems.size(); for (int i = 0; i < N; i++) { Item item = mItems.get(i); if (item.getUri() != null) { if (items == null) { items = new ArrayList<>(N); } items.add(new Item(item.getUri())); } else if (item.getIntent() != null) { if (items == null) { items = new ArrayList<>(N); } items.add(new Item(item.getIntent().cloneForCreatorToken())); } } if (items == null || items.isEmpty()) return null; return new ClipData(new ClipDescription("", new String[0]), items); } /** * Create a new ClipData holding data of the type * {@link ClipDescription#MIMETYPE_TEXT_PLAIN}. Loading
core/java/android/content/Intent.java +59 −1 Original line number Diff line number Diff line Loading @@ -7969,6 +7969,24 @@ public class Intent implements Parcelable, Cloneable { return new Intent(this, COPY_MODE_FILTER); } /** * Make a copy of all members important to identify an intent with its creator token. * @hide */ public @NonNull Intent cloneForCreatorToken() { Intent clone = new Intent() .setAction(this.mAction) .setDataAndType(this.mData, this.mType) .setPackage(this.mPackage) .setComponent(this.mComponent) .setFlags(this.mFlags & IMMUTABLE_FLAGS); if (this.mClipData != null) { clone.setClipData(this.mClipData.cloneOnlyUriItems()); } clone.mCreatorTokenInfo = this.mCreatorTokenInfo; return clone; } /** * Create an intent with a given action. All other fields (data, type, * class) are null. Note that the action <em>must</em> be in a Loading Loading @@ -11684,7 +11702,7 @@ public class Intent implements Parcelable, Cloneable { Log.w(TAG, "Failure filling in extras", e); } } mCreatorTokenInfo = other.mCreatorTokenInfo; fillInCreatorTokenInfo(other.mCreatorTokenInfo, changes); if (mayHaveCopiedUris && mContentUserHint == UserHandle.USER_CURRENT && other.mContentUserHint != UserHandle.USER_CURRENT) { mContentUserHint = other.mContentUserHint; Loading @@ -11692,6 +11710,45 @@ public class Intent implements Parcelable, Cloneable { return changes; } // keep original creator token and merge nested intent keys. private void fillInCreatorTokenInfo(CreatorTokenInfo otherCreatorTokenInfo, int changes) { if (otherCreatorTokenInfo != null && otherCreatorTokenInfo.mNestedIntentKeys != null) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } ArraySet<NestedIntentKey> otherNestedIntentKeys = otherCreatorTokenInfo.mNestedIntentKeys; if (mCreatorTokenInfo.mNestedIntentKeys == null) { mCreatorTokenInfo.mNestedIntentKeys = new ArraySet<>(otherNestedIntentKeys); } else { ArraySet<NestedIntentKey> otherKeys; if ((changes & FILL_IN_CLIP_DATA) == 0) { // If clip data is Not filled in from other, do not merge clip data keys. otherKeys = new ArraySet<>(); int N = otherNestedIntentKeys.size(); for (int i = 0; i < N; i++) { NestedIntentKey key = otherNestedIntentKeys.valueAt(i); if (key.mType != NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) { otherKeys.add(key); } } } else { // If clip data is filled in from other, remove clip data keys from this // creatorTokenInfo and then merge every key from the others. int N = mCreatorTokenInfo.mNestedIntentKeys.size(); for (int i = N - 1; i >= 0; i--) { NestedIntentKey key = mCreatorTokenInfo.mNestedIntentKeys.valueAt(i); if (key.mType == NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA) { mCreatorTokenInfo.mNestedIntentKeys.removeAt(i); } } otherKeys = otherNestedIntentKeys; } mCreatorTokenInfo.mNestedIntentKeys.addAll(otherKeys); } } } /** * Merge the extras data in this intent with that of other supplied intent using the * strategy specified using {@code extrasMerger}. Loading Loading @@ -12228,6 +12285,7 @@ public class Intent implements Parcelable, Cloneable { private IBinder mCreatorToken; // Stores all extra keys whose values are intents for a top level intent. private ArraySet<NestedIntentKey> mNestedIntentKeys; } /** Loading
core/tests/coretests/src/android/content/IntentTest.java +111 −2 Original line number Diff line number Diff line Loading @@ -19,8 +19,9 @@ package android.content; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; Loading @@ -29,6 +30,7 @@ import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.security.Flags; import android.util.ArraySet; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; Loading @@ -40,6 +42,7 @@ import org.junit.runner.RunWith; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; /** * Build/Install/Run: Loading @@ -51,7 +54,6 @@ import java.util.List; public class IntentTest { private static final String TEST_ACTION = "android.content.IntentTest_test"; private static final String TEST_EXTRA_NAME = "testExtraName"; private static final Uri TEST_URI = Uri.parse("content://com.example/people"); @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); Loading Loading @@ -129,4 +131,111 @@ public class IntentTest { } } @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testFillInCreatorTokenInfo() { // case 1: intent does not have creatorTokenInfo; fillinIntent contains creatorTokenInfo Intent intent = new Intent(); Intent fillInIntent = new Intent(); fillInIntent.setCreatorToken(new Binder()); fillInIntent.putExtra("extraKey", new Intent()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); // extra intent keys are merged assertThat(intent.getExtraIntentKeys()).isEqualTo(fillInIntent.getExtraIntentKeys()); // but creator token is not overwritten. assertThat(intent.getCreatorToken()).isNull(); // case 2: Both intent and fillInIntent contains creatorToken, intent's creatorToken is not // overwritten. intent = new Intent(); IBinder creatorToken = new Binder(); intent.setCreatorToken(creatorToken); fillInIntent = new Intent(); fillInIntent.setCreatorToken(new Binder()); intent.fillIn(fillInIntent, 0); assertThat(intent.getCreatorToken()).isEqualTo(creatorToken); // case 3: Contains duplicate extra keys intent = new Intent(); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); Set originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); assertThat(intent.getExtraIntentKeys()).hasSize(3); assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // case 4: Both contains a mixture of extras and clip data. NOT force to fill in clip data. intent = new Intent(); ClipData clipData = ClipData.newIntent("clip", new Intent()); clipData.addItem(new ClipData.Item(new Intent())); intent.setClipData(clipData); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); ClipData fillInClipData = ClipData.newIntent("clip", new Intent()); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInIntent.setClipData(fillInClipData); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, 0); // size is 5 ( 3 extras merged from both + 2 clip data in the original. assertThat(intent.getExtraIntentKeys()).hasSize(5); // all keys from original are kept. assertTrue(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); // Not all keys from fillInIntent are kept - clip data keys are dropped. assertFalse(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // case 5: Both contains a mixture of extras and clip data. Force to fill in clip data. intent = new Intent(); clipData = ClipData.newIntent("clip", new Intent()); clipData.addItem(new ClipData.Item(new Intent())); clipData.addItem(new ClipData.Item(new Intent())); clipData.addItem(new ClipData.Item(new Intent())); intent.setClipData(clipData); intent.putExtra("key1", new Intent()); intent.putExtra("key2", new Intent()); fillInIntent = new Intent(); fillInClipData = ClipData.newIntent("clip", new Intent()); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInClipData.addItem(new ClipData.Item(new Intent())); fillInIntent.setClipData(fillInClipData); fillInIntent.putExtra("key1", new Intent()); fillInIntent.putExtra("key3", new Intent()); intent.collectExtraIntentKeys(); originalIntentKeys = new ArraySet<>(intent.getExtraIntentKeys()); fillInIntent.collectExtraIntentKeys(); intent.fillIn(fillInIntent, Intent.FILL_IN_CLIP_DATA); // size is 6 ( 3 extras merged from both + 3 clip data in the fillInIntent. assertThat(intent.getExtraIntentKeys()).hasSize(6); // all keys from fillInIntent are kept. assertTrue(intent.getExtraIntentKeys().containsAll(fillInIntent.getExtraIntentKeys())); // Not all keys from intent are kept - clip data keys are dropped. assertFalse(intent.getExtraIntentKeys().containsAll(originalIntentKeys)); } }
services/core/java/com/android/server/am/ActivityManagerService.java +37 −4 Original line number Diff line number Diff line Loading @@ -192,6 +192,7 @@ import static com.android.systemui.shared.Flags.enableHomeDelay; import android.Manifest; import android.Manifest.permission; import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.PermissionMethod; Loading Loading @@ -19228,6 +19229,11 @@ public class ActivityManagerService extends IActivityManager.Stub return mKeyFields.mCreatorPackage; } @VisibleForTesting public @NonNull Key getKeyFields() { return mKeyFields; } public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; Loading Loading @@ -19271,9 +19277,13 @@ public class ActivityManagerService extends IActivityManager.Stub this.mFlags = intent.getFlags() & Intent.IMMUTABLE_FLAGS; ClipData clipData = intent.getClipData(); if (clipData != null) { this.mClipDataUris = new ArrayList<>(clipData.getItemCount()); for (int i = 0; i < clipData.getItemCount(); i++) { this.mClipDataUris.add(clipData.getItemAt(i).getUri()); clipData = clipData.cloneOnlyUriItems(); if (clipData != null) { List<Uri> clipDataUris = new ArrayList<>(); clipData.collectUris(clipDataUris); if (!clipDataUris.isEmpty()) { this.mClipDataUris = clipDataUris; } } } } Loading Loading @@ -19375,11 +19385,34 @@ public class ActivityManagerService extends IActivityManager.Stub String creatorPackage) { if (IntentCreatorToken.isValid(intent)) return null; IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent); return createOrGetIntentCreatorToken(intent, key); } /** * @hide */ @EnforcePermission("android.permission.INTERACT_ACROSS_USERS_FULL") public IBinder refreshIntentCreatorToken(Intent intent) { refreshIntentCreatorToken_enforcePermission(); IBinder binder = intent.getCreatorToken(); if (binder instanceof IntentCreatorToken) { IntentCreatorToken token = (IntentCreatorToken) binder; IntentCreatorToken.Key key = new IntentCreatorToken.Key(token.getCreatorUid(), token.getCreatorPackage(), intent); return createOrGetIntentCreatorToken(intent, key); } else { throw new IllegalArgumentException("intent does not contain a creator token."); } } private static IntentCreatorToken createOrGetIntentCreatorToken(Intent intent, IntentCreatorToken.Key key) { IntentCreatorToken token; synchronized (sIntentCreatorTokenCache) { WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key); if (ref == null || ref.get() == null) { token = new IntentCreatorToken(creatorUid, creatorPackage, intent); token = new IntentCreatorToken(key.mCreatorUid, key.mCreatorPackage, intent); sIntentCreatorTokenCache.put(key, token.mRef); } else { token = ref.get();