summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author John Wu <topjohnwu@google.com> 2024-02-16 23:38:56 +0000
committer John Wu <topjohnwu@google.com> 2024-02-21 00:05:19 +0000
commit3c40b1d4dec9adfda08f3699df5be1e35c5dbfe4 (patch)
treebfa7b2fc41afa2b2494f317e2f2f85bf2216ebdb
parent9720fee8c5624c06236a322a9d1ec0b490474389 (diff)
Introduce intent flag EXTENDED_FLAG_FILTER_MISMATCH
Introduce new flag value EXTENDED_FLAG_INTENT_MISMATCH to allow the system to inform app components whether it matches its intent filter. Because all bit values of mFlag is used, we need to introduce a new flag field to store additional information in intents. This information is exposed as a public API through the method Intent#isMismatchingFilter(). The actual enforcement for blocking mismatching intents are disabled for V and is planned for future releases. Bug: 293560872 Test: atest CtsContentTestCases:IntentTest Test: atest CtsContentTestCases --test-filter '.*IntentMismatchFlag' Change-Id: Ic79b7b7d00b937d15f05d32706ac6b7e63c22419
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/test-current.txt3
-rw-r--r--core/api/test-lint-baseline.txt6
-rw-r--r--core/java/android/content/Intent.java116
-rw-r--r--core/proto/android/content/intent.proto1
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java31
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerServiceUtils.java7
-rw-r--r--services/core/java/com/android/server/wm/ActivityStartController.java3
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java11
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java12
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...