diff options
| author | 2022-09-14 17:03:56 +0000 | |
|---|---|---|
| committer | 2022-09-14 17:03:56 +0000 | |
| commit | 44c0ac96b9e1c081f64f8c747641ed972734672b (patch) | |
| tree | 2476ffd38207933a738eacb39f2363553d65de47 | |
| parent | 3acc70a6ed9a31ee073fbd226fc2003e6cedef47 (diff) | |
| parent | 80a78df01ee1d0931ae5691a1c39f54036e0acc3 (diff) | |
Merge "Verify that the component is exported"
7 files changed, 155 insertions, 2 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d899dab8a61d..8f5457abdca4 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4216,13 +4216,23 @@ public class ActivityManager { } }*/ + /** @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 7ca62542aaca..9f3f761eac65 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -577,6 +577,14 @@ public abstract class PackageManagerInternal { 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 6e52d21e6b43..2ccba56ed283 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -215,6 +215,7 @@ import android.appwidget.AppWidgetManager; 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; @@ -585,6 +586,17 @@ public class ActivityManagerService extends IActivityManager.Stub 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[])} @@ -12379,6 +12391,58 @@ public class ActivityManagerService extends IActivityManager.Stub } /** + * 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. @@ -14217,6 +14281,8 @@ public class ActivityManagerService extends IActivityManager.Stub } } + 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 @@ -14319,6 +14385,8 @@ public class ActivityManagerService extends IActivityManager.Stub 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 34d6d292dd19..65e7ce1466dd 100644 --- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java +++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java @@ -465,6 +465,20 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal { 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 c2fd637cfbd0..fada5778b634 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 android.util.Slog; 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.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -100,6 +101,38 @@ final class ResolveIntentHelper { 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 @@ final class ResolveIntentHelper { @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 class ResolveIntentHelper { 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 dc91c1597128..28cd0016b019 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 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // (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 85e5bfd84f9f..2b0e76cbc9bd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -379,6 +379,8 @@ public class ActivityStarterTests extends WindowTestsBase { 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 |