Merge "Whitelist for package broadcasts" into rvc-dev
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9f51edb..39fc18d 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -403,4 +403,20 @@
      * Called by DevicePolicyManagerService to set the uid of the device owner.
      */
     public abstract void setDeviceOwnerUid(int uid);
+
+    /**
+     * Sends a broadcast, assuming the caller to be the system and allowing the inclusion of an
+     * approved whitelist of app Ids >= {@link android.os.Process#FIRST_APPLICATION_UID} that the
+     * broadcast my be sent to; any app Ids < {@link android.os.Process#FIRST_APPLICATION_UID} are
+     * automatically whitelisted.
+     *
+     * @see com.android.server.am.ActivityManagerService#broadcastIntentWithFeature(
+     *      IApplicationThread, String, Intent, String, IIntentReceiver, int, String, Bundle,
+     *      String[], int, Bundle, boolean, boolean, int)
+     */
+    public abstract int broadcastIntent(Intent intent,
+            IIntentReceiver resultTo,
+            String[] requiredPermissions, boolean serialized,
+            int userId, int[] appIdWhitelist);
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 73d6fff..bd905da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -15568,7 +15568,7 @@
     }
 
     private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
-            int callingUid, int[] users) {
+            int callingUid, int[] users, int[] broadcastWhitelist) {
         // TODO: come back and remove this assumption to triage all broadcasts
         int pmFlags = STOCK_PM_FLAGS | MATCH_DEBUG_TRIAGED_MISSING;
 
@@ -15644,6 +15644,15 @@
         } catch (RemoteException ex) {
             // pm is in same process, this will never happen.
         }
+        if (receivers != null && broadcastWhitelist != null) {
+            for (int i = receivers.size() - 1; i >= 0; i--) {
+                final int uid = receivers.get(i).activityInfo.applicationInfo.uid;
+                if (uid >= Process.FIRST_APPLICATION_UID
+                        && Arrays.binarySearch(broadcastWhitelist, UserHandle.getAppId(uid)) < 0) {
+                    receivers.remove(i);
+                }
+            }
+        }
         return receivers;
     }
 
@@ -15738,7 +15747,8 @@
         return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
                 resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                 appOp, bOptions, ordered, sticky, callingPid, callingUid, realCallingUid,
-                realCallingPid, userId, false /* allowBackgroundActivityStarts */);
+                realCallingPid, userId, false /* allowBackgroundActivityStarts */,
+                null /*broadcastWhitelist*/);
     }
 
     @GuardedBy("this")
@@ -15747,7 +15757,8 @@
             IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
-            int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
+            int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
+            @Nullable int[] broadcastWhitelist) {
         intent = new Intent(intent);
 
         final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -15756,6 +15767,12 @@
             intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
         }
 
+        if (userId == UserHandle.USER_ALL && broadcastWhitelist != null) {
+                Slog.e(TAG, "broadcastWhitelist only applies when sending to individual users. "
+                        + "Assuming restrictive whitelist.");
+                broadcastWhitelist = new int[]{};
+        }
+
         // By default broadcasts do not go to stopped apps.
         intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
 
@@ -16242,7 +16259,8 @@
         // Need to resolve the intent to interested receivers...
         if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                  == 0) {
-            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
+            receivers = collectReceiverComponents(
+                    intent, resolvedType, callingUid, users, broadcastWhitelist);
         }
         if (intent.getComponent() == null) {
             if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
@@ -16272,6 +16290,17 @@
 
         if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                 + " replacePending=" + replacePending);
+        if (registeredReceivers != null && broadcastWhitelist != null) {
+            // if a uid whitelist was provided, remove anything in the application space that wasn't
+            // in it.
+            for (int i = registeredReceivers.size() - 1; i >= 0; i--) {
+                final int uid = registeredReceivers.get(i).owningUid;
+                if (uid >= Process.FIRST_APPLICATION_UID
+                        && Arrays.binarySearch(broadcastWhitelist, UserHandle.getAppId(uid)) < 0) {
+                    registeredReceivers.remove(i);
+                }
+            }
+        }
 
         int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
         if (!ordered && NR > 0) {
@@ -16555,7 +16584,8 @@
                 return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
                         resultTo, resultCode, resultData, resultExtras, requiredPermissions,
                         OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
-                        realCallingPid, userId, allowBackgroundActivityStarts);
+                        realCallingPid, userId, allowBackgroundActivityStarts,
+                        null /*broadcastWhitelist*/);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -19186,6 +19216,32 @@
         }
 
         @Override
+        public int broadcastIntent(Intent intent,
+                IIntentReceiver resultTo,
+                String[] requiredPermissions,
+                boolean serialized, int userId, int[] appIdWhitelist) {
+            synchronized (ActivityManagerService.this) {
+                intent = verifyBroadcastLocked(intent);
+
+                final int callingPid = Binder.getCallingPid();
+                final int callingUid = Binder.getCallingUid();
+                final long origId = Binder.clearCallingIdentity();
+                try {
+                    return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/,
+                            null /*callerPackage*/, null /*callingFeatureId*/, intent,
+                            null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/,
+                            null /*resultExtras*/, requiredPermissions, AppOpsManager.OP_NONE,
+                            null /*options*/, serialized, false /*sticky*/, callingPid, callingUid,
+                            callingUid, callingPid, userId, false /*allowBackgroundStarts*/,
+                            appIdWhitelist);
+                } finally {
+                    Binder.restoreCallingIdentity(origId);
+                }
+            }
+
+        }
+
+        @Override
         public ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
                 boolean fgRequired, String callingPackage, @Nullable String callingFeatureId,
                 int userId, boolean allowBackgroundActivityStarts)
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 690b9f7..805d918 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -55,6 +55,7 @@
 import com.android.server.pm.parsing.pkg.AndroidPackage;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -507,7 +508,65 @@
     private static boolean isSystemSigned(@NonNull PackageParser.SigningDetails sysSigningDetails,
             PackageSetting pkgSetting) {
         return pkgSetting.isSystem()
-            && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
+                && pkgSetting.signatures.mSigningDetails.signaturesMatchExactly(sysSigningDetails);
+    }
+
+    private static void sort(int[] uids, int nextUidIndex) {
+        Arrays.sort(uids, 0, nextUidIndex);
+    }
+
+    /**
+     * Fetches all app Ids that a given setting is currently visible to, per provided user. This
+     * only includes UIDs >= {@link Process#FIRST_APPLICATION_UID} as all other UIDs can already see
+     * all applications.
+     *
+     * If the setting is visible to all UIDs, null is returned. If an app is not visible to any
+     * applications, the int array will be empty.
+     *
+     * @param users the set of users that should be evaluated for this calculation
+     * @param existingSettings the set of all package settings that currently exist on device
+     * @return a SparseArray mapping userIds to a sorted int array of appIds that may view the
+     *         provided setting or null if the app is visible to all and no whitelist should be
+     *         applied.
+     */
+    @Nullable
+    public SparseArray<int[]> getVisibilityWhitelist(PackageSetting setting, int[] users,
+            ArrayMap<String, PackageSetting> existingSettings) {
+        if (mForceQueryable.contains(setting.appId)) {
+            return null;
+        }
+        // let's reserve max memory to limit the number of allocations
+        SparseArray<int[]> result = new SparseArray<>(users.length);
+        for (int u = 0; u < users.length; u++) {
+            final int userId = users[u];
+            int[] appIds = new int[existingSettings.size()];
+            int[] buffer = null;
+            int whitelistSize = 0;
+            for (int i = existingSettings.size() - 1; i >= 0; i--) {
+                final PackageSetting existingSetting = existingSettings.valueAt(i);
+                final int existingAppId = existingSetting.appId;
+                if (existingAppId < Process.FIRST_APPLICATION_UID) {
+                    continue;
+                }
+                final int loc = Arrays.binarySearch(appIds, 0, whitelistSize, existingAppId);
+                if (loc >= 0) {
+                    continue;
+                }
+                final int existingUid = UserHandle.getUid(userId, existingAppId);
+                if (!shouldFilterApplication(existingUid, existingSetting, setting, userId)) {
+                    if (buffer == null) {
+                        buffer = new int[appIds.length];
+                    }
+                    final int insert = ~loc;
+                    System.arraycopy(appIds, insert, buffer, 0, whitelistSize - insert);
+                    appIds[insert] = existingUid;
+                    System.arraycopy(buffer, 0, appIds, insert + 1, whitelistSize - insert);
+                    whitelistSize++;
+                }
+            }
+            result.put(userId, Arrays.copyOf(appIds, whitelistSize));
+        }
+        return result;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8ef1182..414449d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -136,6 +136,7 @@
 import android.annotation.UserIdInt;
 import android.annotation.WorkerThread;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.ApplicationPackageManager;
 import android.app.BroadcastOptions;
@@ -882,6 +883,7 @@
         private final Singleton<UserManagerService> mUserManagerProducer;
         private final Singleton<Settings> mSettingsProducer;
         private final Singleton<ActivityTaskManagerInternal> mActivityTaskManagerProducer;
+        private final Singleton<ActivityManagerInternal> mActivityManagerInternalProducer;
         private final Singleton<DeviceIdleInternal> mLocalDeviceIdleController;
         private final Singleton<StorageManagerInternal> mStorageManagerInternalProducer;
         private final Singleton<NetworkPolicyManagerInternal> mNetworkPolicyManagerProducer;
@@ -900,6 +902,7 @@
                 Producer<UserManagerService> userManagerProducer,
                 Producer<Settings> settingsProducer,
                 Producer<ActivityTaskManagerInternal> activityTaskManagerProducer,
+                Producer<ActivityManagerInternal> activityManagerInternalProducer,
                 Producer<DeviceIdleInternal> deviceIdleControllerProducer,
                 Producer<StorageManagerInternal> storageManagerInternalProducer,
                 Producer<NetworkPolicyManagerInternal> networkPolicyManagerProducer,
@@ -920,6 +923,7 @@
             mUserManagerProducer = new Singleton<>(userManagerProducer);
             mSettingsProducer = new Singleton<>(settingsProducer);
             mActivityTaskManagerProducer = new Singleton<>(activityTaskManagerProducer);
+            mActivityManagerInternalProducer = new Singleton<>(activityManagerInternalProducer);
             mLocalDeviceIdleController = new Singleton<>(deviceIdleControllerProducer);
             mStorageManagerInternalProducer = new Singleton<>(storageManagerInternalProducer);
             mNetworkPolicyManagerProducer = new Singleton<>(networkPolicyManagerProducer);
@@ -984,6 +988,10 @@
             return mActivityTaskManagerProducer.get(this, mPackageManager);
         }
 
+        public ActivityManagerInternal getActivityManagerInternal() {
+            return mActivityManagerInternalProducer.get(this, mPackageManager);
+        }
+
         public DeviceIdleInternal getLocalDeviceIdleController() {
             return mLocalDeviceIdleController.get(this, mPackageManager);
         }
@@ -2119,16 +2127,23 @@
                 }
                 extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType);
                 // Send to all running apps.
+                final SparseArray<int[]> newBroadcastWhitelist;
+
+                synchronized (mLock) {
+                    newBroadcastWhitelist = mAppsFilter.getVisibilityWhitelist(
+                            getPackageSettingInternal(res.name, Process.SYSTEM_UID),
+                            updateUserIds, mSettings.mPackages);
+                }
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                         extras, 0 /*flags*/,
                         null /*targetPackage*/, null /*finishedReceiver*/,
-                        updateUserIds, instantUserIds);
+                        updateUserIds, instantUserIds, newBroadcastWhitelist);
                 if (installerPackageName != null) {
                     // Send to the installer, even if it's not running.
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             installerPackageName, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds);
+                            updateUserIds, instantUserIds, null /* broadcastWhitelist */);
                 }
                 // if the required verifier is defined, but, is not the installer of record
                 // for the package, it gets notified
@@ -2138,7 +2153,7 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, 0 /*flags*/,
                             mRequiredVerifierPackage, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds);
+                            updateUserIds, instantUserIds, null /* broadcastWhitelist */);
                 }
                 // If package installer is defined, notify package installer about new
                 // app installed
@@ -2146,7 +2161,7 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                             extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND /*flags*/,
                             mRequiredInstallerPackage, null /*finishedReceiver*/,
-                            firstUserIds, instantUserIds);
+                            firstUserIds, instantUserIds, null /* broadcastWhitelist */);
                 }
 
                 // Send replaced for users that don't see the package for the first time
@@ -2154,23 +2169,24 @@
                     sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                             packageName, extras, 0 /*flags*/,
                             null /*targetPackage*/, null /*finishedReceiver*/,
-                            updateUserIds, instantUserIds);
+                            updateUserIds, instantUserIds, res.removedInfo.broadcastWhitelist);
                     if (installerPackageName != null) {
                         sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                 extras, 0 /*flags*/,
                                 installerPackageName, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds);
+                                updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
                     }
                     if (notifyVerifier) {
                         sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
                                 extras, 0 /*flags*/,
                                 mRequiredVerifierPackage, null /*finishedReceiver*/,
-                                updateUserIds, instantUserIds);
+                                updateUserIds, instantUserIds, null /*broadcastWhitelist*/);
                     }
                     sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                             null /*package*/, null /*extras*/, 0 /*flags*/,
                             packageName /*targetPackage*/,
-                            null /*finishedReceiver*/, updateUserIds, instantUserIds);
+                            null /*finishedReceiver*/, updateUserIds, instantUserIds,
+                            null /*broadcastWhitelist*/);
                 } else if (launchedForRestore && !res.pkg.isSystem()) {
                     // First-install and we did a restore, so we're responsible for the
                     // first-launch broadcast.
@@ -2207,6 +2223,7 @@
                     sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
                 }
             } else if (!ArrayUtils.isEmpty(res.libraryConsumers)) { // if static shared lib
+                int[] allUsers = mInjector.getUserManagerService().getUserIds();
                 for (int i = 0; i < res.libraryConsumers.size(); i++) {
                     AndroidPackage pkg = res.libraryConsumers.get(i);
                     // send broadcast that all consumers of the static shared library have changed
@@ -2512,6 +2529,7 @@
                                 i.getPermissionManagerServiceInternal().getPermissionSettings(),
                                 lock),
                 new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class),
+                new Injector.LocalServicesProducer<>(ActivityManagerInternal.class),
                 new Injector.LocalServicesProducer<>(DeviceIdleInternal.class),
                 new Injector.LocalServicesProducer<>(StorageManagerInternal.class),
                 new Injector.LocalServicesProducer<>(NetworkPolicyManagerInternal.class),
@@ -12213,7 +12231,8 @@
     @Override
     public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
             final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
-            final int[] userIds, int[] instantUserIds) {
+            final int[] userIds, int[] instantUserIds,
+            @Nullable SparseArray<int[]> broadcastWhitelist) {
         mHandler.post(() -> {
             try {
                 final IActivityManager am = ActivityManager.getService();
@@ -12225,10 +12244,10 @@
                     resolvedUserIds = userIds;
                 }
                 doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                        resolvedUserIds, false);
+                        resolvedUserIds, false, broadcastWhitelist);
                 if (instantUserIds != null && instantUserIds != EMPTY_INT_ARRAY) {
                     doSendBroadcast(am, action, pkg, extras, flags, targetPkg, finishedReceiver,
-                            instantUserIds, true);
+                            instantUserIds, true, null);
                 }
             } catch (RemoteException ex) {
             }
@@ -12298,8 +12317,7 @@
      */
     private void doSendBroadcast(IActivityManager am, String action, String pkg, Bundle extras,
             int flags, String targetPkg, IIntentReceiver finishedReceiver,
-            int[] userIds, boolean isInstantApp)
-                    throws RemoteException {
+            int[] userIds, boolean isInstantApp, @Nullable SparseArray<int[]> broadcastWhitelist) {
         for (int id : userIds) {
             final Intent intent = new Intent(action,
                     pkg != null ? Uri.fromParts(PACKAGE_SCHEME, pkg, null) : null);
@@ -12326,9 +12344,10 @@
                         + intent.toShortString(false, true, false, false)
                         + " " + intent.getExtras(), here);
             }
-            am.broadcastIntentWithFeature(null, null, intent, null, finishedReceiver,
-                    0, null, null, requiredPermissions, android.app.AppOpsManager.OP_NONE,
-                    null, finishedReceiver != null, false, id);
+            mInjector.getActivityManagerInternal().broadcastIntent(
+                    intent, finishedReceiver, requiredPermissions,
+                    finishedReceiver != null, id,
+                    broadcastWhitelist == null ? null : broadcastWhitelist.get(id));
         }
     }
 
@@ -12456,7 +12475,10 @@
         extras.putInt(Intent.EXTRA_UID, uid);
 
         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
-                packageName, extras, 0, null, null, userIds, instantUserIds);
+                packageName, extras, 0, null, null, userIds, instantUserIds,
+                mAppsFilter.getVisibilityWhitelist(
+                        getPackageSettingInternal(packageName, Process.SYSTEM_UID),
+                        userIds, mSettings.mPackages));
         if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) {
             mHandler.post(() -> {
                         for (int userId : userIds) {
@@ -12662,7 +12684,7 @@
         extras.putIntArray(Intent.EXTRA_CHANGED_UID_LIST, uidList);
         extras.putInt(Intent.EXTRA_DISTRACTION_RESTRICTIONS, distractionFlags);
         sendPackageBroadcast(Intent.ACTION_DISTRACTING_PACKAGES_CHANGED, null, extras,
-                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null);
+                Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null, new int[]{userId}, null, null);
     }
 
     private void sendPackagesSuspendedForUser(String[] pkgList, int[] uidList, int userId,
@@ -12674,7 +12696,7 @@
                 suspended ? Intent.ACTION_PACKAGES_SUSPENDED
                         : Intent.ACTION_PACKAGES_UNSUSPENDED,
                 null, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY, null, null,
-                new int[] {userId}, null);
+                new int[] {userId}, null, null);
     }
 
     /**
@@ -13065,31 +13087,27 @@
                 ? Intent.ACTION_MY_PACKAGE_SUSPENDED
                 : Intent.ACTION_MY_PACKAGE_UNSUSPENDED;
         mHandler.post(() -> {
-            try {
-                final IActivityManager am = ActivityManager.getService();
-                if (am == null) {
-                    Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
-                            + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
-                    return;
+            final IActivityManager am = ActivityManager.getService();
+            if (am == null) {
+                Slog.wtf(TAG, "IActivityManager null. Cannot send MY_PACKAGE_ "
+                        + (suspended ? "" : "UN") + "SUSPENDED broadcasts");
+                return;
+            }
+            final int[] targetUserIds = new int[] {userId};
+            for (String packageName : affectedPackages) {
+                final Bundle appExtras = suspended
+                        ? getSuspendedPackageAppExtrasInternal(packageName, userId)
+                        : null;
+                final Bundle intentExtras;
+                if (appExtras != null) {
+                    intentExtras = new Bundle(1);
+                    intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
+                } else {
+                    intentExtras = null;
                 }
-                final int[] targetUserIds = new int[] {userId};
-                for (String packageName : affectedPackages) {
-                    final Bundle appExtras = suspended
-                            ? getSuspendedPackageAppExtrasInternal(packageName, userId)
-                            : null;
-                    final Bundle intentExtras;
-                    if (appExtras != null) {
-                        intentExtras = new Bundle(1);
-                        intentExtras.putBundle(Intent.EXTRA_SUSPENDED_PACKAGE_EXTRAS, appExtras);
-                    } else {
-                        intentExtras = null;
-                    }
-                    doSendBroadcast(am, action, null, intentExtras,
-                            Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
-                            targetUserIds, false);
-                }
-            } catch (RemoteException ex) {
-                // Shouldn't happen as AMS is in the same process.
+                doSendBroadcast(am, action, null, intentExtras,
+                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, packageName, null,
+                        targetUserIds, false, null);
             }
         });
     }
@@ -14089,7 +14107,7 @@
     private void sendFirstLaunchBroadcast(String pkgName, String installerPkg,
             int[] userIds, int[] instantUserIds) {
         sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgName, null, 0,
-                installerPkg, null, userIds, instantUserIds);
+                installerPkg, null, userIds, instantUserIds, null /* broadcastWhitelist */);
     }
 
     private abstract class HandlerParams {
@@ -16172,6 +16190,8 @@
                 reconciledPkg.pkgSetting.firstInstallTime = deletedPkgSetting.firstInstallTime;
                 reconciledPkg.pkgSetting.lastUpdateTime = System.currentTimeMillis();
 
+                res.removedInfo.broadcastWhitelist = mAppsFilter.getVisibilityWhitelist(
+                                reconciledPkg.pkgSetting, request.mAllUsers, mSettings.mPackages);
                 if (reconciledPkg.prepareResult.system) {
                     // Remove existing system package
                     removePackageLI(oldPackage, true);
@@ -17986,6 +18006,9 @@
         boolean dataRemoved;
         boolean removedForAllUsers;
         boolean isStaticSharedLib;
+        // a two dimensional array mapping userId to the set of appIds that can receive notice
+        // of package changes
+        SparseArray<int[]> broadcastWhitelist;
         // Clean up resources deleted packages.
         InstallArgs args = null;
         ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
@@ -18019,19 +18042,19 @@
             Bundle extras = new Bundle(2);
             extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
             extras.putBoolean(Intent.EXTRA_REPLACING, true);
-            packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
-                removedPackage, extras, 0, null /*targetPackage*/, null, null, null);
-            packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
-                removedPackage, extras, 0, null /*targetPackage*/, null, null, null);
-            packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
-                null, null, 0, removedPackage, null, null, null);
+            packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage, extras,
+                    0, null /*targetPackage*/, null, null, null, broadcastWhitelist);
+            packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
+                    extras, 0, null /*targetPackage*/, null, null, null, broadcastWhitelist);
+            packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, 0,
+                    removedPackage, null, null, null, null /* broadcastWhitelist */);
             if (installerPackageName != null) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null, null);
+                        installerPackageName, null, null, null, null /* broadcastWhitelist */);
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                         removedPackage, extras, 0 /*flags*/,
-                        installerPackageName, null, null, null);
+                        installerPackageName, null, null, null, null /* broadcastWhitelist */);
             }
         }
 
@@ -18054,17 +18077,16 @@
             if (removedPackage != null) {
                 packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
                     removedPackage, extras, 0, null /*targetPackage*/, null,
-                    broadcastUsers, instantUserIds);
+                    broadcastUsers, instantUserIds, broadcastWhitelist);
                 if (installerPackageName != null) {
                     packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
                             removedPackage, extras, 0 /*flags*/,
-                            installerPackageName, null, broadcastUsers, instantUserIds);
+                            installerPackageName, null, broadcastUsers, instantUserIds, null);
                 }
                 if (dataRemoved && !isRemovedPackageSystemUpdate) {
                     packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
-                        removedPackage, extras,
-                        Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
-                        null, null, broadcastUsers, instantUserIds);
+                            removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,
+                            null, broadcastUsers, instantUserIds, broadcastWhitelist);
                     packageSender.notifyPackageRemoved(removedPackage, removedUid);
                 }
             }
@@ -18077,7 +18099,7 @@
 
                 packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED,
                         null, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
-                    null, null, broadcastUsers, instantUserIds);
+                        null, null, broadcastUsers, instantUserIds, broadcastWhitelist);
             }
         }
 
@@ -20418,8 +20440,7 @@
     }
 
     private void sendPackageChangedBroadcast(String packageName,
-            boolean dontKillApp, ArrayList<String> componentNames, int packageUid,
-            String reason) {
+            boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason) {
         if (DEBUG_INSTALL)
             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
                     + componentNames);
@@ -20442,8 +20463,14 @@
         final boolean isInstantApp = isInstantApp(packageName, userId);
         final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
         final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
+        final SparseArray<int[]> broadcastWhitelist;
+        synchronized (mLock) {
+            broadcastWhitelist = isInstantApp ? null : mAppsFilter.getVisibilityWhitelist(
+                    getPackageSettingInternal(packageName, Process.SYSTEM_UID),
+                    userIds, mSettings.mPackages);
+        }
         sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED,  packageName, extras, flags, null, null,
-                userIds, instantUserIds);
+                userIds, instantUserIds, broadcastWhitelist);
     }
 
     @Override
@@ -21679,7 +21706,7 @@
     }
 
     private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacing,
-            String[] pkgList, int uidArr[], IIntentReceiver finishedReceiver) {
+            String[] pkgList, int[] uidArr, IIntentReceiver finishedReceiver) {
         int size = pkgList.length;
         if (size > 0) {
             // Send broadcasts here
@@ -21693,7 +21720,8 @@
             }
             String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
                     : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
-            sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null);
+            // TODO: not sure how to handle this one.
+            sendPackageBroadcast(action, null, extras, 0, null, finishedReceiver, null, null, null);
         }
     }
 
@@ -24867,7 +24895,8 @@
      */
     void sendPackageBroadcast(final String action, final String pkg,
         final Bundle extras, final int flags, final String targetPkg,
-        final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds);
+        final IIntentReceiver finishedReceiver, final int[] userIds, int[] instantUserIds,
+        @Nullable SparseArray<int[]> broadcastWhitelist);
     void sendPackageAddedForNewUsers(String packageName, boolean sendBootCompleted,
         boolean includeStopped, int appId, int[] userIds, int[] instantUserIds);
     void notifyPackageAdded(String packageName, int uid);
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 22591c6..5109de5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -17,6 +17,7 @@
 package com.android.server.pm;
 
 
+import static org.hamcrest.Matchers.arrayContaining;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -47,6 +48,7 @@
 import com.android.server.pm.parsing.pkg.PackageImpl;
 import com.android.server.pm.parsing.pkg.ParsedPackage;
 
+import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -56,7 +58,9 @@
 import org.mockito.MockitoAnnotations;
 
 import java.security.cert.CertificateException;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -635,6 +639,57 @@
                 appsFilter.shouldFilterApplication(DUMMY_TARGET_UID, target, instrumentation, 0));
     }
 
+    @Test
+    public void testWhoCanSee() throws Exception {
+        final AppsFilter appsFilter =
+                new AppsFilter(mFeatureConfigMock, new String[]{}, false, null);
+        appsFilter.onSystemReady();
+
+        final int systemAppId = Process.FIRST_APPLICATION_UID - 1;
+        final int seesNothingAppId = Process.FIRST_APPLICATION_UID;
+        final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1;
+        final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;
+        PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
+        PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("some.system.pkg"),
+                seesNothingAppId);
+        PackageSetting hasProvider = simulateAddPackage(appsFilter,
+                pkgWithProvider("com.some.package", "com.some.authority"), hasProviderAppId);
+        PackageSetting queriesProvider = simulateAddPackage(appsFilter,
+                pkgQueriesProvider("com.some.other.package", "com.some.authority"),
+                queriesProviderAppId);
+
+        final int[] systemFilter =
+                appsFilter.getVisibilityWhitelist(system, new int[]{0}, mExisting).get(0);
+        assertThat(Arrays.asList(systemFilter), arrayContaining(systemAppId));
+
+        final int[] seesNothingFilter =
+                appsFilter.getVisibilityWhitelist(seesNothing, new int[]{0}, mExisting).get(0);
+        assertThat(Arrays.asList(seesNothingFilter),
+                arrayContaining(systemAppId, seesNothingAppId));
+
+        final int[] hasProviderFilter =
+                appsFilter.getVisibilityWhitelist(hasProvider, new int[]{0}, mExisting).get(0);
+        assertThat(Arrays.asList(hasProviderFilter),
+                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));
+
+        int[] queriesProviderFilter =
+                appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
+        assertThat(Arrays.asList(queriesProviderFilter),
+                arrayContaining(systemAppId, queriesProviderAppId));
+
+        // provider read
+        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
+
+        // ensure implicit access is included in the filter
+        queriesProviderFilter =
+                appsFilter.getVisibilityWhitelist(queriesProvider, new int[]{0}, mExisting).get(0);
+        assertThat(Arrays.asList(queriesProviderFilter),
+                arrayContaining(systemAppId, hasProviderAppId, queriesProviderAppId));
+    }
+
+    private void assertThat(List<int[]> asList, Matcher<Integer[]> arrayContainingInAnyOrder) {
+    }
+
     private interface WithSettingBuilder {
         PackageSettingBuilder withBuilder(PackageSettingBuilder builder);
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index 9cf6702..0a68688 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -18,6 +18,7 @@
 
 import android.content.IIntentReceiver;
 import android.os.Bundle;
+import android.util.SparseArray;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -47,7 +48,7 @@
             public void sendPackageBroadcast(final String action, final String pkg,
                     final Bundle extras, final int flags, final String targetPkg,
                     final IIntentReceiver finishedReceiver, final int[] userIds,
-                    int[] instantUserIds) {
+                    int[] instantUserIds, SparseArray<int[]> broadcastWhitelist) {
             }
 
             public void sendPackageAddedForNewUsers(String packageName,