diff options
| -rw-r--r-- | core/api/current.txt | 1 | ||||
| -rw-r--r-- | core/api/test-current.txt | 3 | ||||
| -rw-r--r-- | core/api/test-lint-baseline.txt | 6 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 116 | ||||
| -rw-r--r-- | core/proto/android/content/intent.proto | 1 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 31 | ||||
| -rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerServiceUtils.java | 7 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/ActivityStartController.java | 3 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/ActivityStarter.java | 11 | ||||
| -rw-r--r-- | services/core/java/com/android/server/wm/ActivityTaskManagerService.java | 12 |
10 files changed, 172 insertions, 19 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 84fbe1aa37ee..314a5651aee7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -11075,6 +11075,7 @@ package android.content { method public boolean hasCategory(String); method public boolean hasExtra(String); method public boolean hasFileDescriptors(); + method @FlaggedApi("android.security.enforce_intent_filter_match") public boolean isMismatchingFilter(); method public static android.content.Intent makeMainActivity(android.content.ComponentName); method public static android.content.Intent makeMainSelectorActivity(String, String); method public static android.content.Intent makeRestartActivityTask(android.content.ComponentName); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 334d9e63bb98..45a17371eb10 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1032,7 +1032,10 @@ package android.content { } public class Intent implements java.lang.Cloneable android.os.Parcelable { + method @NonNull public android.content.Intent addExtendedFlags(int); + method public int getExtendedFlags(); field public static final String ACTION_USER_STOPPED = "android.intent.action.USER_STOPPED"; + field public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1; // 0x1 } public class SyncAdapterType implements android.os.Parcelable { diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index c1181f5b8233..d45fb13a4f95 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -2017,6 +2017,12 @@ UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, Stri New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],android.content.AttributionSource) UnflaggedApi: android.content.AttributionSource#AttributionSource(int, int, String, String, android.os.IBinder, String[], int, android.content.AttributionSource): New API must be flagged with @FlaggedApi: constructor android.content.AttributionSource(int,int,String,String,android.os.IBinder,String[],int,android.content.AttributionSource) +UnflaggedApi: android.content.Intent#EXTENDED_FLAG_FILTER_MISMATCH: + New API must be flagged with @FlaggedApi: field android.content.Intent.EXTENDED_FLAG_FILTER_MISMATCH +UnflaggedApi: android.content.Intent#addExtendedFlags(int): + New API must be flagged with @FlaggedApi: method android.content.Intent.addExtendedFlags(int) +UnflaggedApi: android.content.Intent#getExtendedFlags(): + New API must be flagged with @FlaggedApi: method android.content.Intent.getExtendedFlags() UnflaggedApi: android.content.pm.UserInfo#isCommunalProfile(): New API must be flagged with @FlaggedApi: method android.content.pm.UserInfo.isCommunalProfile() UnflaggedApi: android.content.pm.UserInfo#isPrivateProfile(): diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0bcbb8e1868c..fd2af99b6a7e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -7656,6 +7656,22 @@ public class Intent implements Parcelable, Cloneable { /** @hide */ public static final int LOCAL_FLAG_FROM_SYSTEM = 1 << 5; + /** @hide */ + @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { + EXTENDED_FLAG_FILTER_MISMATCH, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ExtendedFlags {} + + /** + * This flag is not normally set by application code, but set for you by the system if + * an external intent does not match the receiving component's intent filter. + * + * @hide + */ + @TestApi + public static final int EXTENDED_FLAG_FILTER_MISMATCH = 1 << 0; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // toUri() and parseUri() options. @@ -7775,6 +7791,7 @@ public class Intent implements Parcelable, Cloneable { private int mFlags; /** Set of in-process flags which are never parceled */ private int mLocalFlags; + private int mExtendedFlags; private ArraySet<String> mCategories; @UnsupportedAppUsage private Bundle mExtras; @@ -7834,6 +7851,7 @@ public class Intent implements Parcelable, Cloneable { if (copyMode != COPY_MODE_FILTER) { this.mFlags = o.mFlags; + this.mExtendedFlags = o.mExtendedFlags; this.mContentUserHint = o.mContentUserHint; this.mLaunchToken = o.mLaunchToken; if (o.mSourceBounds != null) { @@ -8161,12 +8179,17 @@ public class Intent implements Parcelable, Cloneable { // launch flags else if (uri.startsWith("launchFlags=", i)) { - intent.mFlags = Integer.decode(value).intValue(); + intent.mFlags = Integer.decode(value); if ((flags& URI_ALLOW_UNSAFE) == 0) { intent.mFlags &= ~IMMUTABLE_FLAGS; } } + // extended flags + else if (uri.startsWith("extendedLaunchFlags=", i)) { + intent.mExtendedFlags = Integer.decode(value); + } + // package else if (uri.startsWith("package=", i)) { intent.mPackage = value; @@ -8374,7 +8397,7 @@ public class Intent implements Parcelable, Cloneable { isIntentFragment = true; i += 12; int j = uri.indexOf(')', i); - intent.mFlags = Integer.decode(uri.substring(i, j)).intValue(); + intent.mFlags = Integer.decode(uri.substring(i, j)); if ((flags& URI_ALLOW_UNSAFE) == 0) { intent.mFlags &= ~IMMUTABLE_FLAGS; } @@ -9868,6 +9891,22 @@ public class Intent implements Parcelable, Cloneable { return mFlags; } + /** + * Retrieve any extended flags associated with this intent. You will + * normally just set them with {@link #setExtendedFlags} and let the system + * take the appropriate action with them. + * + * @return The currently set extended flags. + * @see #addExtendedFlags + * @see #removeExtendedFlags + * + * @hide + */ + @TestApi + public @ExtendedFlags int getExtendedFlags() { + return mExtendedFlags; + } + /** @hide */ @UnsupportedAppUsage public boolean isExcludingStopped() { @@ -11198,6 +11237,23 @@ public class Intent implements Parcelable, Cloneable { } /** + * Add additional extended flags to the intent (or with existing flags value). + * + * @param flags The new flags to set. + * @return Returns the same Intent object, for chaining multiple calls into + * a single statement. + * @see #getExtendedFlags + * @see #removeExtendedFlags + * + * @hide + */ + @TestApi + public @NonNull Intent addExtendedFlags(@ExtendedFlags int flags) { + mExtendedFlags |= flags; + return this; + } + + /** * Remove these flags from the intent. * * @param flags The flags to remove. @@ -11210,6 +11266,19 @@ public class Intent implements Parcelable, Cloneable { } /** + * Remove these extended flags from the intent. + * + * @param flags The flags to remove. + * @see #getExtendedFlags + * @see #addExtendedFlags + * + * @hide + */ + public void removeExtendedFlags(@ExtendedFlags int flags) { + mExtendedFlags &= ~flags; + } + + /** * (Usually optional) Set an explicit application package name that limits * the components this Intent will resolve to. If left to the default * value of null, all components in all applications will considered. @@ -11513,6 +11582,7 @@ public class Intent implements Parcelable, Cloneable { changes |= FILL_IN_COMPONENT; } mFlags |= other.mFlags; + mExtendedFlags |= other.mExtendedFlags; if (other.mSourceBounds != null && (mSourceBounds == null || (flags&FILL_IN_SOURCE_BOUNDS) != 0)) { mSourceBounds = new Rect(other.mSourceBounds); @@ -11748,6 +11818,13 @@ public class Intent implements Parcelable, Cloneable { first = false; b.append("flg=0x").append(Integer.toHexString(mFlags)); } + if (mExtendedFlags != 0) { + if (!first) { + b.append(' '); + } + first = false; + b.append("xflg=0x").append(Integer.toHexString(mExtendedFlags)); + } if (mPackage != null) { if (!first) { b.append(' '); @@ -11846,6 +11923,9 @@ public class Intent implements Parcelable, Cloneable { if (mFlags != 0) { proto.write(IntentProto.FLAG, "0x" + Integer.toHexString(mFlags)); } + if (mExtendedFlags != 0) { + proto.write(IntentProto.EXTENDED_FLAG, "0x" + Integer.toHexString(mExtendedFlags)); + } if (mPackage != null) { proto.write(IntentProto.PACKAGE, mPackage); } @@ -12019,6 +12099,10 @@ public class Intent implements Parcelable, Cloneable { if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } + if (mExtendedFlags != 0) { + uri.append("extendedLaunchFlags=0x").append(Integer.toHexString(mExtendedFlags)) + .append(';'); + } if (mPackage != null && !mPackage.equals(defPackage)) { uri.append("package=").append(Uri.encode(mPackage)).append(';'); } @@ -12068,6 +12152,7 @@ public class Intent implements Parcelable, Cloneable { out.writeString8(mType); out.writeString8(mIdentifier); out.writeInt(mFlags); + out.writeInt(mExtendedFlags); out.writeString8(mPackage); ComponentName.writeToParcel(mComponent, out); @@ -12136,6 +12221,7 @@ public class Intent implements Parcelable, Cloneable { mType = in.readString8(); mIdentifier = in.readString8(); mFlags = in.readInt(); + mExtendedFlags = in.readInt(); mPackage = in.readString8(); mComponent = ComponentName.readFromParcel(in); @@ -12560,6 +12646,32 @@ public class Intent implements Parcelable, Cloneable { } /** + * Whether the intent mismatches all intent filters declared in the receiving component. + * <p> + * When a component receives an intent, normally obtainable through the following methods: + * <ul> + * <li> {@link BroadcastReceiver#onReceive(Context, Intent)} + * <li> {@link Activity#getIntent()} + * <li> {@link Activity#onNewIntent)} + * <li> {@link android.app.Service#onStartCommand(Intent, int, int)} + * <li> {@link android.app.Service#onBind(Intent)} + * </ul> + * The developer can call this method to check if this intent does not match any of its + * declared intent filters. A non-matching intent can be delivered when the intent sender + * explicitly set the component through {@link #setComponent} or {@link #setClassName}. + * <p> + * This method always returns {@code false} if the intent originated from within the same + * application or the system, because these cases are always exempted from security checks. + * + * @return Returns true if the intent does not match any intent filters declared in the + * receiving component. + */ + @FlaggedApi(android.security.Flags.FLAG_ENFORCE_INTENT_FILTER_MATCH) + public boolean isMismatchingFilter() { + return (mExtendedFlags & EXTENDED_FLAG_FILTER_MISMATCH) != 0; + } + + /** * @hide */ @android.ravenwood.annotation.RavenwoodThrow diff --git a/core/proto/android/content/intent.proto b/core/proto/android/content/intent.proto index 1d1f88b01838..3607865cc591 100644 --- a/core/proto/android/content/intent.proto +++ b/core/proto/android/content/intent.proto @@ -55,6 +55,7 @@ message IntentProto { optional string data = 3 [ (.android.privacy).dest = DEST_EXPLICIT ]; optional string type = 4; optional string flag = 5; + optional string extended_flag = 14; optional string package = 6; optional ComponentNameProto component = 7; optional string source_bounds = 8; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bbd3ad0bf5ce..2dd2f8fde797 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5478,6 +5478,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } intents[i] = new Intent(intent); + intents[i].removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } } if (resolvedTypes != null && resolvedTypes.length != intents.length) { @@ -13666,9 +13667,13 @@ public class ActivityManagerService extends IActivityManager.Stub throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); enforceAllowedToStartOrBindServiceIfSdkSandbox(service); - // Refuse possible leaked file descriptors - if (service != null && service.hasFileDescriptors() == true) { - throw new IllegalArgumentException("File descriptors passed in Intent"); + if (service != null) { + // Refuse possible leaked file descriptors + if (service.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + // Remove existing mismatch flag so it can be properly updated later + service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } if (callingPackage == null) { @@ -13897,9 +13902,13 @@ public class ActivityManagerService extends IActivityManager.Stub enforceNotIsolatedCaller("bindService"); enforceAllowedToStartOrBindServiceIfSdkSandbox(service); - // Refuse possible leaked file descriptors - if (service != null && service.hasFileDescriptors() == true) { - throw new IllegalArgumentException("File descriptors passed in Intent"); + if (service != null) { + // Refuse possible leaked file descriptors + if (service.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + // Remove existing mismatch flag so it can be properly updated later + service.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } if (callingPackage == null) { @@ -15800,9 +15809,13 @@ public class ActivityManagerService extends IActivityManager.Stub } final Intent verifyBroadcastLocked(Intent intent) { - // Refuse possible leaked file descriptors - if (intent != null && intent.hasFileDescriptors() == true) { - throw new IllegalArgumentException("File descriptors passed in Intent"); + if (intent != null) { + // Refuse possible leaked file descriptors + if (intent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + // Remove existing mismatch flag so it can be properly updated later + intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } int flags = intent.getFlags(); diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 4f9ed03dc8e7..0a3dfc08686a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -46,7 +46,7 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; +import android.compat.annotation.Disabled; import android.compat.annotation.Overridable; import android.content.Context; import android.content.Intent; @@ -200,7 +200,7 @@ public class PackageManagerServiceUtils { */ @Overridable @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) + @Disabled private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188; /** @@ -1246,6 +1246,9 @@ public class PackageManagerServiceUtils { ActivityManagerUtils.logUnsafeIntentEvent( UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__EXPLICIT_INTENT_FILTER_UNMATCH, filterCallingUid, intent, resolvedType, enforce); + if (android.security.Flags.enforceIntentFilterMatch()) { + intent.addExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); + } if (enforce) { Slog.w(TAG, "Intent does not match component's intent filter: " + intent); Slog.w(TAG, "Access blocked: " + comp.getComponentName()); diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 2c492035140b..1df332d71313 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -434,6 +434,9 @@ public class ActivityStartController { // Don't modify the client's object! intent = new Intent(intent); + // Remove existing mismatch flag so it can be properly updated later + intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); + // Collect information about the target of the Intent. ActivityInfo aInfo = mSupervisor.resolveActivity(intent, resolvedTypes[i], 0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid, diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index cfd049508e65..256e89b507a3 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -713,9 +713,14 @@ class ActivityStarter { try { onExecutionStarted(); - // Refuse possible leaked file descriptors - if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); + if (mRequest.intent != null) { + // Refuse possible leaked file descriptors + if (mRequest.intent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + + // Remove existing mismatch flag so it can be properly updated later + mRequest.intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } final LaunchingState launchingState; diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3637ab129c36..bbbe29a9e022 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1317,9 +1317,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { IBinder allowlistToken, Intent fillInIntent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle bOptions) { enforceNotIsolatedCaller("startActivityIntentSender"); - // Refuse possible leaked file descriptors - if (fillInIntent != null && fillInIntent.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); + if (fillInIntent != null) { + // Refuse possible leaked file descriptors + if (fillInIntent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + // Remove existing mismatch flag so it can be properly updated later + fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); } if (!(target instanceof PendingIntentRecord)) { @@ -1363,6 +1367,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return false; } intent = new Intent(intent); + // Remove existing mismatch flag so it can be properly updated later + intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); // The caller is not allowed to change the data. intent.setDataAndType(r.intent.getData(), r.intent.getType()); // And we are resetting to find the next component... |