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