Loading core/java/android/content/Intent.java +89 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_AC import static android.content.ContentProvider.maybeAddUserId; import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.security.Flags.FLAG_FRP_ENFORCEMENT; import static android.security.Flags.preventIntentRedirect; import android.Manifest; import android.accessibilityservice.AccessibilityService; Loading Loading @@ -7687,9 +7688,17 @@ public class Intent implements Parcelable, Cloneable { /** @hide */ public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5; /** * This flag indicates the creator token of this intent has been verified. * * @hide */ public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6; /** @hide */ @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { EXTENDED_FLAG_FILTER_MISMATCH, EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN, }) @Retention(RetentionPolicy.SOURCE) public @interface ExtendedFlags {} Loading @@ -7703,6 +7712,13 @@ public class Intent implements Parcelable, Cloneable { @TestApi public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0; /** * This flag indicates the creator token of this intent is either missing or invalid. * * @hide */ public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1; // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. Loading Loading @@ -7870,6 +7886,7 @@ public class Intent implements Parcelable, Cloneable { this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mOriginalIntent = o.mOriginalIntent; this.mCreatorTokenInfo = o.mCreatorTokenInfo; if (o.mCategories != null) { this.mCategories = new ArraySet<>(o.mCategories); Loading Loading @@ -12176,6 +12193,60 @@ public class Intent implements Parcelable, Cloneable { return (mExtras != null) ? mExtras.describeContents() : 0; } private static class CreatorTokenInfo { // Stores a creator token for an intent embedded as an extra intent in a top level intent, private IBinder mCreatorToken; // Stores all extra keys whose values are intents for a top level intent. private ArraySet<String> mExtraIntentKeys; } private @Nullable CreatorTokenInfo mCreatorTokenInfo; /** @hide */ public void removeCreatorTokenInfo() { mCreatorTokenInfo = null; } /** @hide */ public @Nullable IBinder getCreatorToken() { return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken; } /** @hide */ public Set<String> getExtraIntentKeys() { return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys; } /** @hide */ public void setCreatorToken(@NonNull IBinder creatorToken) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } mCreatorTokenInfo.mCreatorToken = creatorToken; } /** * Collects keys in the extra bundle whose value are intents. * @hide */ public void collectExtraIntentKeys() { if (!preventIntentRedirect()) return; if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { if (mExtras.get(key) instanceof Intent) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } if (mCreatorTokenInfo.mExtraIntentKeys == null) { mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>(); } mCreatorTokenInfo.mExtraIntentKeys.add(key); } } } } public void writeToParcel(Parcel out, int flags) { out.writeString8(mAction); Uri.writeToParcel(out, mData); Loading Loading @@ -12225,6 +12296,16 @@ public class Intent implements Parcelable, Cloneable { } else { out.writeInt(0); } if (preventIntentRedirect()) { if (mCreatorTokenInfo == null) { out.writeInt(0); } else { out.writeInt(1); out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken); out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys); } } } public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR Loading Loading @@ -12282,6 +12363,14 @@ public class Intent implements Parcelable, Cloneable { if (in.readInt() != 0) { mOriginalIntent = new Intent(in); } if (preventIntentRedirect()) { if (in.readInt() != 0) { mCreatorTokenInfo = new CreatorTokenInfo(); mCreatorTokenInfo.mCreatorToken = in.readStrongBinder(); mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null); } } } /** Loading core/java/android/security/responsible_apis_flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -52,3 +52,11 @@ flag { description: "Opt the system into enforcement of BAL" bug: "339403750" } flag { name: "prevent_intent_redirect" namespace: "responsible_apis" description: "Prevent intent redirect attacks" bug: "361143368" is_fixed_read_only: true } No newline at end of file core/tests/coretests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ android_test { "mockito-target-extended-minus-junit4", "TestParameterInjector", "android.content.res.flags-aconfig-java", "android.security.flags-aconfig-java", ], libs: [ Loading core/tests/coretests/src/android/content/IntentTest.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.platform.test.annotations.Presubmit; 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 androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; /** * Build/Install/Run: * atest FrameworksCoreTests:IntentTest */ @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) 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(); @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testReadFromParcelWithExtraIntentKeys() { Intent intent = new Intent("TEST_ACTION"); intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION)); intent.putExtra(TEST_EXTRA_NAME + "2", 1); intent.collectExtraIntentKeys(); final Parcel parcel = Parcel.obtain(); intent.writeToParcel(parcel, 0); parcel.setDataPosition(0); final Intent target = new Intent(); target.readFromParcel(parcel); assertEquals(intent.getAction(), target.getAction()); assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys()); assertThat(intent.getExtraIntentKeys()).hasSize(1); } @Test public void testCreatorTokenInfo() { Intent intent = new Intent(TEST_ACTION); IBinder creatorToken = new Binder(); intent.setCreatorToken(creatorToken); assertThat(intent.getCreatorToken()).isEqualTo(creatorToken); intent.removeCreatorTokenInfo(); assertThat(intent.getCreatorToken()).isNull(); } @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testCollectExtraIntentKeys() { Intent intent = new Intent(TEST_ACTION); Intent extraIntent = new Intent(TEST_ACTION, TEST_URI); intent.putExtra(TEST_EXTRA_NAME, extraIntent); intent.collectExtraIntentKeys(); assertThat(intent.getExtraIntentKeys()).hasSize(1); assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME); } } services/core/java/com/android/server/am/ActivityManagerService.java +87 −1 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ import android.appwidget.AppWidgetManagerInternal; import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentCaptureOptions; Loading Loading @@ -418,7 +419,6 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.AlarmManagerInternal; import com.android.server.BootReceiver; import com.android.server.DeviceIdleInternal; Loading @@ -438,6 +438,7 @@ import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.criticalevents.CriticalEventLog; import com.android.server.firewall.IntentFirewall; import com.android.server.graphics.fonts.FontManagerInternal; Loading Loading @@ -482,6 +483,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; Loading @@ -499,6 +501,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; Loading Loading @@ -19085,4 +19088,87 @@ public class ActivityManagerService extends IActivityManager.Stub Freezer getFreezer() { return mFreezer; } // Set of IntentCreatorToken objects that are currently active. private static final Map<IntentCreatorToken.Key, WeakReference<IntentCreatorToken>> sIntentCreatorTokenCache = new ConcurrentHashMap<>(); /** * A binder token used to keep track of which app created the intent. This token can be used to * defend against intent redirect attacks. It stores uid of the intent creator and key fields of * the intent to make it impossible for attacker to fake uid with a malicious intent. * * @hide */ public static final class IntentCreatorToken extends Binder { @NonNull private final Key mKeyFields; public IntentCreatorToken(int creatorUid, Intent intent) { super(); this.mKeyFields = new Key(creatorUid, intent); } public int getCreatorUid() { return mKeyFields.mCreatorUid; } /** {@hide} */ public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; if (binder instanceof IntentCreatorToken) { token = (IntentCreatorToken) binder; } return token != null && token.mKeyFields.equals( new Key(token.mKeyFields.mCreatorUid, intent)); } private static class Key { private Key(int creatorUid, Intent intent) { this.mCreatorUid = creatorUid; this.mAction = intent.getAction(); this.mData = intent.getData(); this.mType = intent.getType(); this.mPackage = intent.getPackage(); this.mComponent = intent.getComponent(); 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()); } } } private final int mCreatorUid; private final String mAction; private final Uri mData; private final String mType; private final String mPackage; private final ComponentName mComponent; private final int mFlags; private List<Uri> mClipDataUris; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals( mAction, key.mAction) && Objects.equals(mData, key.mData) && Objects.equals(mType, key.mType) && Objects.equals(mPackage, key.mPackage) && Objects.equals(mComponent, key.mComponent) && Objects.equals(mClipDataUris, key.mClipDataUris); } @Override public int hashCode() { return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent, mFlags, mClipDataUris); } } } } Loading
core/java/android/content/Intent.java +89 −0 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_AC import static android.content.ContentProvider.maybeAddUserId; import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE; import static android.security.Flags.FLAG_FRP_ENFORCEMENT; import static android.security.Flags.preventIntentRedirect; import android.Manifest; import android.accessibilityservice.AccessibilityService; Loading Loading @@ -7687,9 +7688,17 @@ public class Intent implements Parcelable, Cloneable { /** @hide */ public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5; /** * This flag indicates the creator token of this intent has been verified. * * @hide */ public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6; /** @hide */ @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { EXTENDED_FLAG_FILTER_MISMATCH, EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN, }) @Retention(RetentionPolicy.SOURCE) public @interface ExtendedFlags {} Loading @@ -7703,6 +7712,13 @@ public class Intent implements Parcelable, Cloneable { @TestApi public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0; /** * This flag indicates the creator token of this intent is either missing or invalid. * * @hide */ public static final int EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN = 1 << 1; // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. Loading Loading @@ -7870,6 +7886,7 @@ public class Intent implements Parcelable, Cloneable { this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mOriginalIntent = o.mOriginalIntent; this.mCreatorTokenInfo = o.mCreatorTokenInfo; if (o.mCategories != null) { this.mCategories = new ArraySet<>(o.mCategories); Loading Loading @@ -12176,6 +12193,60 @@ public class Intent implements Parcelable, Cloneable { return (mExtras != null) ? mExtras.describeContents() : 0; } private static class CreatorTokenInfo { // Stores a creator token for an intent embedded as an extra intent in a top level intent, private IBinder mCreatorToken; // Stores all extra keys whose values are intents for a top level intent. private ArraySet<String> mExtraIntentKeys; } private @Nullable CreatorTokenInfo mCreatorTokenInfo; /** @hide */ public void removeCreatorTokenInfo() { mCreatorTokenInfo = null; } /** @hide */ public @Nullable IBinder getCreatorToken() { return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mCreatorToken; } /** @hide */ public Set<String> getExtraIntentKeys() { return mCreatorTokenInfo == null ? null : mCreatorTokenInfo.mExtraIntentKeys; } /** @hide */ public void setCreatorToken(@NonNull IBinder creatorToken) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } mCreatorTokenInfo.mCreatorToken = creatorToken; } /** * Collects keys in the extra bundle whose value are intents. * @hide */ public void collectExtraIntentKeys() { if (!preventIntentRedirect()) return; if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) { for (String key : mExtras.keySet()) { if (mExtras.get(key) instanceof Intent) { if (mCreatorTokenInfo == null) { mCreatorTokenInfo = new CreatorTokenInfo(); } if (mCreatorTokenInfo.mExtraIntentKeys == null) { mCreatorTokenInfo.mExtraIntentKeys = new ArraySet<>(); } mCreatorTokenInfo.mExtraIntentKeys.add(key); } } } } public void writeToParcel(Parcel out, int flags) { out.writeString8(mAction); Uri.writeToParcel(out, mData); Loading Loading @@ -12225,6 +12296,16 @@ public class Intent implements Parcelable, Cloneable { } else { out.writeInt(0); } if (preventIntentRedirect()) { if (mCreatorTokenInfo == null) { out.writeInt(0); } else { out.writeInt(1); out.writeStrongBinder(mCreatorTokenInfo.mCreatorToken); out.writeArraySet(mCreatorTokenInfo.mExtraIntentKeys); } } } public static final @android.annotation.NonNull Parcelable.Creator<Intent> CREATOR Loading Loading @@ -12282,6 +12363,14 @@ public class Intent implements Parcelable, Cloneable { if (in.readInt() != 0) { mOriginalIntent = new Intent(in); } if (preventIntentRedirect()) { if (in.readInt() != 0) { mCreatorTokenInfo = new CreatorTokenInfo(); mCreatorTokenInfo.mCreatorToken = in.readStrongBinder(); mCreatorTokenInfo.mExtraIntentKeys = (ArraySet<String>) in.readArraySet(null); } } } /** Loading
core/java/android/security/responsible_apis_flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -52,3 +52,11 @@ flag { description: "Opt the system into enforcement of BAL" bug: "339403750" } flag { name: "prevent_intent_redirect" namespace: "responsible_apis" description: "Prevent intent redirect attacks" bug: "361143368" is_fixed_read_only: true } No newline at end of file
core/tests/coretests/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,7 @@ android_test { "mockito-target-extended-minus-junit4", "TestParameterInjector", "android.content.res.flags-aconfig-java", "android.security.flags-aconfig-java", ], libs: [ Loading
core/tests/coretests/src/android/content/IntentTest.java 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.content; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import android.net.Uri; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; import android.platform.test.annotations.Presubmit; 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 androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; /** * Build/Install/Run: * atest FrameworksCoreTests:IntentTest */ @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) 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(); @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testReadFromParcelWithExtraIntentKeys() { Intent intent = new Intent("TEST_ACTION"); intent.putExtra(TEST_EXTRA_NAME, new Intent(TEST_ACTION)); intent.putExtra(TEST_EXTRA_NAME + "2", 1); intent.collectExtraIntentKeys(); final Parcel parcel = Parcel.obtain(); intent.writeToParcel(parcel, 0); parcel.setDataPosition(0); final Intent target = new Intent(); target.readFromParcel(parcel); assertEquals(intent.getAction(), target.getAction()); assertEquals(intent.getExtraIntentKeys(), target.getExtraIntentKeys()); assertThat(intent.getExtraIntentKeys()).hasSize(1); } @Test public void testCreatorTokenInfo() { Intent intent = new Intent(TEST_ACTION); IBinder creatorToken = new Binder(); intent.setCreatorToken(creatorToken); assertThat(intent.getCreatorToken()).isEqualTo(creatorToken); intent.removeCreatorTokenInfo(); assertThat(intent.getCreatorToken()).isNull(); } @Test @RequiresFlagsEnabled(Flags.FLAG_PREVENT_INTENT_REDIRECT) public void testCollectExtraIntentKeys() { Intent intent = new Intent(TEST_ACTION); Intent extraIntent = new Intent(TEST_ACTION, TEST_URI); intent.putExtra(TEST_EXTRA_NAME, extraIntent); intent.collectExtraIntentKeys(); assertThat(intent.getExtraIntentKeys()).hasSize(1); assertThat(intent.getExtraIntentKeys()).contains(TEST_EXTRA_NAME); } }
services/core/java/com/android/server/am/ActivityManagerService.java +87 −1 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ import android.appwidget.AppWidgetManagerInternal; import android.content.AttributionSource; import android.content.AutofillOptions; import android.content.BroadcastReceiver; import android.content.ClipData; import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentCaptureOptions; Loading Loading @@ -418,7 +419,6 @@ import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.MemInfoReader; import com.android.internal.util.Preconditions; import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.AlarmManagerInternal; import com.android.server.BootReceiver; import com.android.server.DeviceIdleInternal; Loading @@ -438,6 +438,7 @@ import com.android.server.am.LowMemDetector.MemFactor; import com.android.server.appop.AppOpsService; import com.android.server.compat.PlatformCompat; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.crashrecovery.CrashRecoveryHelper; import com.android.server.criticalevents.CriticalEventLog; import com.android.server.firewall.IntentFirewall; import com.android.server.graphics.fonts.FontManagerInternal; Loading Loading @@ -482,6 +483,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.time.Instant; import java.time.ZoneId; import java.time.ZonedDateTime; Loading @@ -499,6 +501,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; Loading Loading @@ -19085,4 +19088,87 @@ public class ActivityManagerService extends IActivityManager.Stub Freezer getFreezer() { return mFreezer; } // Set of IntentCreatorToken objects that are currently active. private static final Map<IntentCreatorToken.Key, WeakReference<IntentCreatorToken>> sIntentCreatorTokenCache = new ConcurrentHashMap<>(); /** * A binder token used to keep track of which app created the intent. This token can be used to * defend against intent redirect attacks. It stores uid of the intent creator and key fields of * the intent to make it impossible for attacker to fake uid with a malicious intent. * * @hide */ public static final class IntentCreatorToken extends Binder { @NonNull private final Key mKeyFields; public IntentCreatorToken(int creatorUid, Intent intent) { super(); this.mKeyFields = new Key(creatorUid, intent); } public int getCreatorUid() { return mKeyFields.mCreatorUid; } /** {@hide} */ public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; if (binder instanceof IntentCreatorToken) { token = (IntentCreatorToken) binder; } return token != null && token.mKeyFields.equals( new Key(token.mKeyFields.mCreatorUid, intent)); } private static class Key { private Key(int creatorUid, Intent intent) { this.mCreatorUid = creatorUid; this.mAction = intent.getAction(); this.mData = intent.getData(); this.mType = intent.getType(); this.mPackage = intent.getPackage(); this.mComponent = intent.getComponent(); 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()); } } } private final int mCreatorUid; private final String mAction; private final Uri mData; private final String mType; private final String mPackage; private final ComponentName mComponent; private final int mFlags; private List<Uri> mClipDataUris; @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals( mAction, key.mAction) && Objects.equals(mData, key.mData) && Objects.equals(mType, key.mType) && Objects.equals(mPackage, key.mPackage) && Objects.equals(mComponent, key.mComponent) && Objects.equals(mClipDataUris, key.mClipDataUris); } @Override public int hashCode() { return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent, mFlags, mClipDataUris); } } } }