Verify that the component is exported
CL for tests: ag/19776394
Bug: 229362273
Test: atest
This reverts commit 24a489a25a9672b012f3527ca9c8b2bc1053bb6e.
Change-Id: I41c48a4127f66d81085c5f58140d640ae30cd949
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index e4c758e..30dbec7 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -4208,13 +4208,23 @@
}
}*/
+ /** @hide
+ * Determines whether the given UID can access unexported components
+ * @param uid the calling UID
+ * @return true if the calling UID is ROOT or SYSTEM
+ */
+ public static boolean canAccessUnexportedComponents(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID);
+ }
+
/** @hide */
@UnsupportedAppUsage
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
final int appId = UserHandle.getAppId(uid);
- if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+ if (canAccessUnexportedComponents(uid)) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 3d8dc14..0ce49d0 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -578,6 +578,14 @@
int filterCallingUid);
/**
+ * Resolves an exported activity intent, allowing instant apps to be resolved.
+ */
+ public abstract ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
+ int filterCallingUid);
+
+ /**
* Resolves a service intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index be0335e..7e497fd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -214,6 +214,7 @@
import android.appwidget.AppWidgetManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
@@ -583,6 +584,17 @@
private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
/**
+ * Apps targeting Android U and above will need to export components in order to invoke them
+ * through implicit intents.
+ *
+ * If a component is not exported and invoked, it will be removed from the list of receivers.
+ * This applies specifically to activities and broadcasts.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273;
+
+ /**
* The maximum number of bytes that {@link #setProcessStateSummary} accepts.
*
* @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
@@ -12332,6 +12344,58 @@
}
/**
+ * Filters out non-exported components in a given list of broadcast filters
+ * @param intent the original intent
+ * @param callingUid the calling UID
+ * @param query the list of broadcast filters
+ * @param platformCompat the instance of platform compat
+ */
+ private static void filterNonExportedComponents(Intent intent, int callingUid,
+ List query, PlatformCompat platformCompat, String callerPackage) {
+ if (query == null
+ || intent.getPackage() != null
+ || intent.getComponent() != null
+ || ActivityManager.canAccessUnexportedComponents(callingUid)) {
+ return;
+ }
+ for (int i = query.size() - 1; i >= 0; i--) {
+ String componentInfo;
+ ResolveInfo resolveInfo;
+ BroadcastFilter broadcastFilter;
+ if (query.get(i) instanceof ResolveInfo) {
+ resolveInfo = (ResolveInfo) query.get(i);
+ if (resolveInfo.getComponentInfo().exported) {
+ continue;
+ }
+ componentInfo = resolveInfo.getComponentInfo()
+ .getComponentName().flattenToShortString();
+ } else if (query.get(i) instanceof BroadcastFilter) {
+ broadcastFilter = (BroadcastFilter) query.get(i);
+ if (broadcastFilter.exported) {
+ continue;
+ }
+ componentInfo = broadcastFilter.packageName;
+ } else {
+ continue;
+ }
+ if (!platformCompat.isChangeEnabledByUid(
+ IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, callingUid)) {
+ Slog.w(TAG, "Non-exported component not filtered out "
+ + "(will be filtered out once the app targets U+)- intent: "
+ + intent.getAction() + ", component: "
+ + componentInfo + ", sender: "
+ + callerPackage);
+ return;
+ }
+ Slog.w(TAG, "Non-exported component filtered out - intent: "
+ + intent.getAction() + ", component: "
+ + componentInfo + ", sender: "
+ + callerPackage);
+ query.remove(i);
+ }
+ }
+
+ /**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
@@ -14173,6 +14237,8 @@
}
}
+ filterNonExportedComponents(intent, callingUid, registeredReceivers,
+ mPlatformCompat, callerPackage);
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
@@ -14275,6 +14341,8 @@
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
+ filterNonExportedComponents(intent, callingUid, receivers,
+ mPlatformCompat, callerPackage);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index e969d93..8f255fa 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -466,6 +466,20 @@
filterCallingUid);
}
+ /**
+ * @deprecated similar to {@link resolveIntent} but limits the matches to exported components.
+ */
+ @Override
+ @Deprecated
+ public final ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid) {
+ return getResolveIntentHelper().resolveIntentInternal(snapshot(),
+ intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
+ filterCallingUid, true);
+ }
+
@Override
@Deprecated
public final ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index 24ed621..f09e137 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -52,6 +52,7 @@
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.ArrayUtils;
+import com.android.server.am.ActivityManagerService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -100,6 +101,38 @@
mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
}
+ private static void filterNonExportedComponents(Intent intent, int filterCallingUid,
+ List<ResolveInfo> query, PlatformCompat platformCompat, Computer computer) {
+ if (query == null
+ || intent.getPackage() != null
+ || intent.getComponent() != null
+ || ActivityManager.canAccessUnexportedComponents(filterCallingUid)) {
+ return;
+ }
+ AndroidPackage caller = computer.getPackage(filterCallingUid);
+ String callerPackage = caller == null ? "Not specified" : caller.getPackageName();
+ for (int i = query.size() - 1; i >= 0; i--) {
+ if (!query.get(i).getComponentInfo().exported) {
+ if (!platformCompat.isChangeEnabledByUid(
+ ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
+ filterCallingUid)) {
+ Slog.w(TAG, "Non-exported component not filtered out "
+ + "(will be filtered out once the app targets U+)- intent: "
+ + intent.getAction() + ", component: "
+ + query.get(i).getComponentInfo()
+ .getComponentName().flattenToShortString()
+ + ", starter: " + callerPackage);
+ return;
+ }
+ Slog.w(TAG, "Non-exported component filtered out - intent: "
+ + intent.getAction() + ", component: "
+ + query.get(i).getComponentInfo().getComponentName().flattenToShortString()
+ + ", starter: " + callerPackage);
+ query.remove(i);
+ }
+ }
+ }
+
/**
* Normally instant apps can only be resolved when they're visible to the caller.
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
@@ -109,6 +142,20 @@
@PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
+ return resolveIntentInternal(computer, intent, resolvedType, flags,
+ privateResolveFlags, userId, resolveForStart, filterCallingUid, false);
+ }
+
+ /**
+ * Normally instant apps can only be resolved when they're visible to the caller.
+ * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+ * since we need to allow the system to start any installed application.
+ * Allows picking exported components only.
+ */
+ public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
@@ -124,6 +171,10 @@
final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
resolveForStart, true /*allowDynamicSplits*/);
+ if (exportedComponentsOnly) {
+ filterNonExportedComponents(intent, filterCallingUid, query,
+ mPlatformCompat, computer);
+ }
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final boolean queryMayBeFiltered =
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index dc91c15..28cd001 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -744,7 +744,7 @@
// (e.g. AMS.startActivityAsUser).
final long token = Binder.clearCallingIdentity();
try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
+ return mService.getPackageManagerInternalLocked().resolveIntentExported(
intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
filterCallingUid);
} finally {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index cd087e6..017de0f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -371,6 +371,8 @@
doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
anyInt(), anyBoolean(), anyInt());
+ doReturn(null).when(mMockPackageManager).resolveIntentExported(any(), any(),
+ anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt());
doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
// Never review permissions