Apply package visibility to LauncherApps.Callback

Applications could know package related events via registering and
implementing the LaunchersApps.Callback. We should also apply the
visibility to LauncherApps.Callback like what we did for Broadcast-
Receiver.

For removed one, using a new protected broadcast with the visibility
allow list in the extra instead since the PackageSetting is already
deleted and wouldn't be able to get its visibility allow list.

Bug: 143946569
Test: atest AppEnumerationTests
Test: atest ShortcutManagerLauncherCallbackTest
Test: atest IncrementalLoadingProgressTest
Test: atest NonIncrementalLoadingProgressTest
Change-Id: If457e2d7892d9d4fbb8cf0ca230de423526eeb10
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 71cd6f1..39933a9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2569,6 +2569,32 @@
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
     /**
+     * Broadcast Action: An existing application package has been removed from
+     * the device. The data contains the name of the package and the visibility
+     * allow list. The package that is being removed does <em>not</em> receive
+     * this Intent.
+     * <ul>
+     * <li> {@link #EXTRA_UID} containing the integer uid previously assigned
+     * to the package.
+     * <li> {@link #EXTRA_DATA_REMOVED} is set to true if the entire
+     * application -- data and code -- is being removed.
+     * <li> {@link #EXTRA_REPLACING} is set to true if this will be followed
+     * by an {@link #ACTION_PACKAGE_ADDED} broadcast for the same package.
+     * <li> {@link #EXTRA_USER_INITIATED} containing boolean field to signal
+     * that the application was removed with the user-initiated action.
+     * <li> {@link #EXTRA_VISIBILITY_ALLOW_LIST} containing an int array to
+     * indicate the visibility allow list.
+     * </ul>
+     *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
+     * @hide This broadcast is used internally by the system.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_PACKAGE_REMOVED_INTERNAL =
+            "android.intent.action.PACKAGE_REMOVED_INTERNAL";
+    /**
      * Broadcast Action: An existing application package has been completely
      * removed from the device.  The data contains the name of the package.
      * This is like {@link #ACTION_PACKAGE_REMOVED}, but only set when
@@ -6122,6 +6148,16 @@
      */
     public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID";
 
+    /**
+     * Used as an int array extra field in
+     * {@link android.content.Intent#ACTION_PACKAGE_REMOVED_INTERNAL}
+     * intents to indicate that visibility allow list of this removed package.
+     *
+     * @hide
+     */
+    public static final String EXTRA_VISIBILITY_ALLOW_LIST =
+            "android.intent.extra.VISIBILITY_ALLOW_LIST";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 93f2de6..6169945 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -40,6 +40,7 @@
     <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
     <protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED_INTERNAL" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
     <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_LOADED" />
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index edd43af..47d1629 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -37,9 +37,11 @@
 import android.app.admin.DevicePolicyManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.content.LocusId;
 import android.content.pm.ActivityInfo;
@@ -95,6 +97,7 @@
 import com.android.server.wm.ActivityTaskManagerInternal;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -146,10 +149,13 @@
         private final ActivityManagerInternal mActivityManagerInternal;
         private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
         private final ShortcutServiceInternal mShortcutServiceInternal;
+        private final PackageManagerInternal mPackageManagerInternal;
         private final PackageCallbackList<IOnAppsChangedListener> mListeners
                 = new PackageCallbackList<IOnAppsChangedListener>();
         private final DevicePolicyManager mDpm;
 
+        private final PackageRemovedListener mPackageRemovedListener =
+                new PackageRemovedListener();
         private final MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
 
         @GuardedBy("mListeners")
@@ -175,6 +181,8 @@
                     LocalServices.getService(ActivityTaskManagerInternal.class));
             mShortcutServiceInternal = Objects.requireNonNull(
                     LocalServices.getService(ShortcutServiceInternal.class));
+            mPackageManagerInternal = Objects.requireNonNull(
+                    LocalServices.getService(PackageManagerInternal.class));
             mShortcutServiceInternal.addListener(mPackageMonitor);
             mShortcutChangeHandler = new ShortcutChangeHandler(mUserManagerInternal);
             mShortcutServiceInternal.addShortcutChangeCallback(mShortcutChangeHandler);
@@ -294,6 +302,11 @@
          */
         private void startWatchingPackageBroadcasts() {
             if (!mIsWatchingPackageBroadcasts) {
+                final IntentFilter filter = new IntentFilter();
+                filter.addAction(Intent.ACTION_PACKAGE_REMOVED_INTERNAL);
+                filter.addDataScheme("package");
+                mContext.registerReceiverAsUser(mPackageRemovedListener, UserHandle.ALL, filter,
+                        /* broadcastPermission= */ null, mCallbackHandler);
                 mPackageMonitor.register(mContext, UserHandle.ALL, true, mCallbackHandler);
                 mIsWatchingPackageBroadcasts = true;
             }
@@ -307,6 +320,7 @@
                 Log.d(TAG, "Stopped watching for packages");
             }
             if (mIsWatchingPackageBroadcasts) {
+                mContext.unregisterReceiver(mPackageRemovedListener);
                 mPackageMonitor.unregister();
                 mIsWatchingPackageBroadcasts = false;
             }
@@ -395,9 +409,8 @@
             if (!canAccessProfile(user.getIdentifier(), "cannot get shouldHideFromSuggestions")) {
                 return false;
             }
-            final PackageManagerInternal pmi = LocalServices.getService(
-                    PackageManagerInternal.class);
-            int flags = pmi.getDistractingPackageRestrictions(packageName, user.getIdentifier());
+            final int flags = mPackageManagerInternal.getDistractingPackageRestrictions(packageName,
+                    user.getIdentifier());
             return (flags & PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS) != 0;
         }
 
@@ -433,16 +446,14 @@
 
                 final ArrayList<LauncherActivityInfoInternal> result = new ArrayList<>(
                         launcherActivities.getList());
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
                 if (packageName != null) {
                     // If this hidden app should not be shown, return the original list.
                     // Otherwise, inject hidden activity that forwards user to app details page.
                     if (result.size() > 0) {
                         return launcherActivities;
                     }
-                    ApplicationInfo appInfo = pmInt.getApplicationInfo(packageName, /*flags*/ 0,
-                            callingUid, user.getIdentifier());
+                    final ApplicationInfo appInfo = mPackageManagerInternal.getApplicationInfo(
+                            packageName, /* flags= */ 0, callingUid, user.getIdentifier());
                     if (shouldShowSyntheticActivity(user, appInfo)) {
                         LauncherActivityInfoInternal info = getHiddenAppActivityInfo(packageName,
                                 callingUid, user);
@@ -456,8 +467,9 @@
                 for (LauncherActivityInfoInternal info : result) {
                     visiblePackages.add(info.getActivityInfo().packageName);
                 }
-                List<ApplicationInfo> installedPackages = pmInt.getInstalledApplications(0,
-                        user.getIdentifier(), callingUid);
+                final List<ApplicationInfo> installedPackages =
+                        mPackageManagerInternal.getInstalledApplications(/* flags= */ 0,
+                                user.getIdentifier(), callingUid);
                 for (ApplicationInfo applicationInfo : installedPackages) {
                     if (!visiblePackages.contains(applicationInfo.packageName)) {
                         if (!shouldShowSyntheticActivity(user, applicationInfo)) {
@@ -483,9 +495,7 @@
             if (isManagedProfileAdmin(user, appInfo.packageName)) {
                 return false;
             }
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            final AndroidPackage pkg = pmInt.getPackage(appInfo.packageName);
+            final AndroidPackage pkg = mPackageManagerInternal.getPackage(appInfo.packageName);
             if (pkg == null) {
                 // Should not happen, but we shouldn't be failing if it does
                 return false;
@@ -501,13 +511,11 @@
         }
 
         private boolean hasDefaultEnableLauncherActivity(@NonNull String packageName) {
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
             final Intent matchIntent = new Intent(Intent.ACTION_MAIN);
             matchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
             matchIntent.setPackage(packageName);
-            final List<ResolveInfo> infoList = pmInt.queryIntentActivities(matchIntent,
-                    matchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+            final List<ResolveInfo> infoList = mPackageManagerInternal.queryIntentActivities(
+                    matchIntent, matchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                     PackageManager.MATCH_DISABLED_COMPONENTS, Binder.getCallingUid(),
                     getCallingUserId());
             final int size = infoList.size();
@@ -548,9 +556,7 @@
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
-                final ActivityInfo activityInfo = pmInt.getActivityInfo(component,
+                final ActivityInfo activityInfo = mPackageManagerInternal.getActivityInfo(component,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                         callingUid, user.getIdentifier());
@@ -561,8 +567,9 @@
                     // should not happen
                     return null;
                 }
-                final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
-                            component.getPackageName(), callingUid, user.getIdentifier());
+                final IncrementalStatesInfo incrementalStatesInfo =
+                        mPackageManagerInternal.getIncrementalStatesInfo(component.getPackageName(),
+                                callingUid, user.getIdentifier());
                 if (incrementalStatesInfo == null) {
                     // package does not exist; should not happen
                     return null;
@@ -598,9 +605,7 @@
 
         private List<LauncherActivityInfoInternal> queryIntentLauncherActivities(
                 Intent intent, int callingUid, UserHandle user) {
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
-            List<ResolveInfo> apps = pmInt.queryIntentActivities(intent,
+            final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(intent,
                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                     PackageManager.MATCH_DIRECT_BOOT_AWARE
                             | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
@@ -614,8 +619,9 @@
                     // should not happen
                     continue;
                 }
-                final IncrementalStatesInfo incrementalStatesInfo = pmInt.getIncrementalStatesInfo(
-                        packageName, callingUid, user.getIdentifier());
+                final IncrementalStatesInfo incrementalStatesInfo =
+                        mPackageManagerInternal.getIncrementalStatesInfo(packageName, callingUid,
+                                user.getIdentifier());
                 if (incrementalStatesInfo == null) {
                     // package doesn't exist any more; should not happen
                     continue;
@@ -639,15 +645,14 @@
             final int callingUid = injectBinderCallingUid();
             final long identity = Binder.clearCallingIdentity();
             try {
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
                 Intent packageIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT)
                         .setPackage(component.getPackageName());
-                List<ResolveInfo> apps = pmInt.queryIntentActivities(packageIntent,
-                        packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
-                        PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                        callingUid, user.getIdentifier());
+                List<ResolveInfo> apps =
+                        mPackageManagerInternal.queryIntentActivities(packageIntent,
+                                packageIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
+                                PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
+                                callingUid, user.getIdentifier());
                 // ensure that the component is present in the list
                 if (!apps.stream().anyMatch(
                         ri -> component.getClassName().equals(ri.activityInfo.name))) {
@@ -712,9 +717,7 @@
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
-                PackageInfo info = pmInt.getPackageInfo(packageName,
+                final PackageInfo info = mPackageManagerInternal.getPackageInfo(packageName,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                         callingUid, user.getIdentifier());
@@ -730,9 +733,8 @@
             if (!canAccessProfile(user.getIdentifier(), "Cannot get launcher extras")) {
                 return null;
             }
-            final PackageManagerInternal pmi =
-                    LocalServices.getService(PackageManagerInternal.class);
-            return pmi.getSuspendedPackageLauncherExtras(packageName, user.getIdentifier());
+            return mPackageManagerInternal.getSuspendedPackageLauncherExtras(packageName,
+                    user.getIdentifier());
         }
 
         @Override
@@ -746,10 +748,8 @@
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
-                ApplicationInfo info = pmInt.getApplicationInfo(packageName, flags,
-                        callingUid, user.getIdentifier());
+                final ApplicationInfo info = mPackageManagerInternal.getApplicationInfo(packageName,
+                        flags, callingUid, user.getIdentifier());
                 return info;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -1043,11 +1043,9 @@
                 return false;
             }
 
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
             final int callingUid = injectBinderCallingUid();
-            final int state = pmInt.getComponentEnabledSetting(component, callingUid,
-                    user.getIdentifier());
+            final int state = mPackageManagerInternal.getComponentEnabledSetting(component,
+                    callingUid, user.getIdentifier());
             switch (state) {
                 case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
                     break; // Need to check the manifest's enabled state.
@@ -1061,7 +1059,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                ActivityInfo info = pmInt.getActivityInfo(component,
+                final ActivityInfo info = mPackageManagerInternal.getActivityInfo(component,
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
                         callingUid, user.getIdentifier());
@@ -1160,12 +1158,11 @@
             final int callingUid = injectBinderCallingUid();
             final long ident = Binder.clearCallingIdentity();
             try {
-                final PackageManagerInternal pmInt =
-                        LocalServices.getService(PackageManagerInternal.class);
                 // Check that the component actually has Intent.CATEGORY_LAUCNCHER
                 // as calling startActivityAsUser ignores the category and just
                 // resolves based on the component if present.
-                List<ResolveInfo> apps = pmInt.queryIntentActivities(launchIntent,
+                final List<ResolveInfo> apps = mPackageManagerInternal.queryIntentActivities(
+                        launchIntent,
                         launchIntent.resolveTypeIfNeeded(mContext.getContentResolver()),
                         PackageManager.MATCH_DIRECT_BOOT_AWARE
                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
@@ -1229,6 +1226,31 @@
                     user.getIdentifier(), debugMsg, false);
         }
 
+        /** Returns whether or not the result to the listener should be filtered. */
+        private boolean isPackageVisibleToListener(String packageName, BroadcastCookie cookie) {
+            return !mPackageManagerInternal.filterAppAccess(packageName, cookie.callingUid,
+                    cookie.user.getIdentifier());
+        }
+
+        /** Returns whether or not the given UID is in allow list */
+        private static boolean isCallingUidAllowed(int[] allowList, int callingUid) {
+            if (allowList == null) {
+                return true;
+            }
+            return Arrays.binarySearch(allowList, callingUid) > -1;
+        }
+
+        private String[] getFilteredPackageNames(String[] packageNames, BroadcastCookie cookie) {
+            final List<String> filteredPackageNames = new ArrayList<>();
+            for (String packageName : packageNames) {
+                if (!isPackageVisibleToListener(packageName, cookie)) {
+                    continue;
+                }
+                filteredPackageNames.add(packageName);
+            }
+            return filteredPackageNames.toArray(new String[filteredPackageNames.size()]);
+        }
+
         private int toShortcutsCacheFlags(int cacheFlags) {
             int ret = 0;
             if (cacheFlags == FLAG_CACHE_NOTIFICATION_SHORTCUTS) {
@@ -1253,19 +1275,17 @@
          * loaded, register loading progress listener.
          */
         void registerLoadingProgressForIncrementalApps() {
-            final PackageManagerInternal pmInt =
-                    LocalServices.getService(PackageManagerInternal.class);
             final List<UserHandle> users = mUm.getUserProfiles();
             if (users == null) {
                 return;
             }
             for (UserHandle user : users) {
-                pmInt.forEachInstalledPackage(pkg -> {
+                mPackageManagerInternal.forEachInstalledPackage(pkg -> {
                     final String packageName = pkg.getPackageName();
-                    if (pmInt.getIncrementalStatesInfo(packageName, Process.myUid(),
-                            user.getIdentifier()).isLoading()) {
-                        pmInt.registerInstalledLoadingProgressCallback(packageName,
-                                new PackageLoadingProgressCallback(packageName, user),
+                    if (mPackageManagerInternal.getIncrementalStatesInfo(packageName,
+                            Process.myUid(), user.getIdentifier()).isLoading()) {
+                        mPackageManagerInternal.registerInstalledLoadingProgressCallback(
+                                packageName, new PackageLoadingProgressCallback(packageName, user),
                                 user.getIdentifier());
                     }
                 }, user.getIdentifier());
@@ -1398,6 +1418,59 @@
             }
         }
 
+        private class PackageRemovedListener extends BroadcastReceiver {
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                        UserHandle.USER_NULL);
+                if (userId == UserHandle.USER_NULL) {
+                    Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+                    return;
+                }
+                final String action = intent.getAction();
+                // Handle onPackageRemoved.
+                if (Intent.ACTION_PACKAGE_REMOVED_INTERNAL.equals(action)) {
+                    final String packageName = getPackageName(intent);
+                    final int[] allowList =
+                            intent.getIntArrayExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST);
+                    // If {@link #EXTRA_REPLACING} is true, that will be onPackageChanged case.
+                    if (packageName != null && !intent.getBooleanExtra(
+                            Intent.EXTRA_REPLACING, /* defaultValue= */ false)) {
+                        final UserHandle user = new UserHandle(userId);
+                        final int n = mListeners.beginBroadcast();
+                        try {
+                            for (int i = 0; i < n; i++) {
+                                final IOnAppsChangedListener listener =
+                                        mListeners.getBroadcastItem(i);
+                                final BroadcastCookie cookie =
+                                        (BroadcastCookie) mListeners.getBroadcastCookie(i);
+                                if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) {
+                                    continue;
+                                }
+                                if (!isCallingUidAllowed(allowList, cookie.callingUid)) {
+                                    continue;
+                                }
+                                try {
+                                    listener.onPackageRemoved(user, packageName);
+                                } catch (RemoteException re) {
+                                    Slog.d(TAG, "Callback failed ", re);
+                                }
+                            }
+                        } finally {
+                            mListeners.finishBroadcast();
+                        }
+                    }
+                }
+            }
+
+            private String getPackageName(Intent intent) {
+                final Uri uri = intent.getData();
+                final String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+                return pkg;
+            }
+        }
+
         private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
 
             // TODO Simplify with lambdas.
@@ -1410,7 +1483,12 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackageAdded")) {
+                            continue;
+                        }
+                        if (!isPackageVisibleToListener(packageName, cookie)) {
+                            continue;
+                        }
                         try {
                             listener.onPackageAdded(user, packageName);
                         } catch (RemoteException re) {
@@ -1421,35 +1499,12 @@
                     mListeners.finishBroadcast();
                 }
                 super.onPackageAdded(packageName, uid);
-                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
-                pmi.registerInstalledLoadingProgressCallback(packageName,
+                mPackageManagerInternal.registerInstalledLoadingProgressCallback(packageName,
                         new PackageLoadingProgressCallback(packageName, user),
                         user.getIdentifier());
             }
 
             @Override
-            public void onPackageRemoved(String packageName, int uid) {
-                UserHandle user = new UserHandle(getChangingUserId());
-                final int n = mListeners.beginBroadcast();
-                try {
-                    for (int i = 0; i < n; i++) {
-                        IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
-                        BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackageRemoved")) continue;
-                        try {
-                            listener.onPackageRemoved(user, packageName);
-                        } catch (RemoteException re) {
-                            Slog.d(TAG, "Callback failed ", re);
-                        }
-                    }
-                } finally {
-                    mListeners.finishBroadcast();
-                }
-
-                super.onPackageRemoved(packageName, uid);
-            }
-
-            @Override
             public void onPackageModified(String packageName) {
                 onPackageChanged(packageName);
                 super.onPackageModified(packageName);
@@ -1462,7 +1517,12 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackageModified")) {
+                            continue;
+                        }
+                        if (!isPackageVisibleToListener(packageName, cookie)) {
+                            continue;
+                        }
                         try {
                             listener.onPackageChanged(user, packageName);
                         } catch (RemoteException re) {
@@ -1482,9 +1542,16 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesAvailable")) {
+                            continue;
+                        }
+                        final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+                        // If all packages are filtered, skip notifying listener.
+                        if (ArrayUtils.isEmpty(filteredPackages)) {
+                            continue;
+                        }
                         try {
-                            listener.onPackagesAvailable(user, packages, isReplacing());
+                            listener.onPackagesAvailable(user, filteredPackages, isReplacing());
                         } catch (RemoteException re) {
                             Slog.d(TAG, "Callback failed ", re);
                         }
@@ -1504,9 +1571,16 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnavailable")) {
+                            continue;
+                        }
+                        final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+                        // If all packages are filtered, skip notifying listener.
+                        if (ArrayUtils.isEmpty(filteredPackages)) {
+                            continue;
+                        }
                         try {
-                            listener.onPackagesUnavailable(user, packages, isReplacing());
+                            listener.onPackagesUnavailable(user, filteredPackages, isReplacing());
                         } catch (RemoteException re) {
                             Slog.d(TAG, "Callback failed ", re);
                         }
@@ -1521,12 +1595,12 @@
             @Override
             public void onPackagesSuspended(String[] packages) {
                 UserHandle user = new UserHandle(getChangingUserId());
-                PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
                 final ArrayList<Pair<String, Bundle>> packagesWithExtras = new ArrayList<>();
                 final ArrayList<String> packagesWithoutExtras = new ArrayList<>();
                 for (String pkg : packages) {
-                    final Bundle launcherExtras = pmi.getSuspendedPackageLauncherExtras(pkg,
-                            user.getIdentifier());
+                    final Bundle launcherExtras =
+                            mPackageManagerInternal.getSuspendedPackageLauncherExtras(pkg,
+                                    user.getIdentifier());
                     if (launcherExtras != null) {
                         packagesWithExtras.add(new Pair<>(pkg, launcherExtras));
                     } else {
@@ -1540,11 +1614,23 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesSuspended")) {
+                            continue;
+                        }
+                        final String[] filteredPackagesWithoutExtras =
+                                getFilteredPackageNames(packages, cookie);
+                        // If all packages are filtered, skip notifying listener.
+                        if (ArrayUtils.isEmpty(filteredPackagesWithoutExtras)) {
+                            continue;
+                        }
                         try {
-                            listener.onPackagesSuspended(user, packagesNullExtras, null);
+                            listener.onPackagesSuspended(user, filteredPackagesWithoutExtras,
+                                    /* launcherExtras= */ null);
                             for (int idx = 0; idx < packagesWithExtras.size(); idx++) {
                                 Pair<String, Bundle> packageExtraPair = packagesWithExtras.get(idx);
+                                if (!isPackageVisibleToListener(packageExtraPair.first, cookie)) {
+                                    continue;
+                                }
                                 listener.onPackagesSuspended(user,
                                         new String[]{packageExtraPair.first},
                                         packageExtraPair.second);
@@ -1566,9 +1652,16 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) continue;
+                        if (!isEnabledProfileOf(cookie.user, user, "onPackagesUnsuspended")) {
+                            continue;
+                        }
+                        final String[] filteredPackages = getFilteredPackageNames(packages, cookie);
+                        // If all packages are filtered, skip notifying listener.
+                        if (ArrayUtils.isEmpty(filteredPackages)) {
+                            continue;
+                        }
                         try {
-                            listener.onPackagesUnsuspended(user, packages);
+                            listener.onPackagesUnsuspended(user, filteredPackages);
                         } catch (RemoteException re) {
                             Slog.d(TAG, "Callback failed ", re);
                         }
@@ -1595,8 +1688,12 @@
                     for (int i = 0; i < n; i++) {
                         IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
                         BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
-                        if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) continue;
-
+                        if (!isEnabledProfileOf(cookie.user, user, "onShortcutChanged")) {
+                            continue;
+                        }
+                        if (!isPackageVisibleToListener(packageName, cookie)) {
+                            continue;
+                        }
                         final int launcherUserId = cookie.user.getIdentifier();
 
                         // Make sure the caller has the permission.
@@ -1668,6 +1765,9 @@
                         if (!isEnabledProfileOf(cookie.user, mUser, "onLoadingProgressChanged")) {
                             continue;
                         }
+                        if (!isPackageVisibleToListener(mPackageName, cookie)) {
+                            continue;
+                        }
                         try {
                             listener.onPackageLoadingProgressChanged(mUser, mPackageName, progress);
                         } catch (RemoteException re) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ba6165c..0d50fb2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -15033,6 +15033,9 @@
                 uid = UserHandle.getUid(id, UserHandle.getAppId(uid));
                 intent.putExtra(Intent.EXTRA_UID, uid);
             }
+            if (broadcastAllowList != null && PLATFORM_PACKAGE_NAME.equals(targetPkg)) {
+                intent.putExtra(Intent.EXTRA_VISIBILITY_ALLOW_LIST, broadcastAllowList.get(id));
+            }
             intent.putExtra(Intent.EXTRA_USER_HANDLE, id);
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT | flags);
             if (DEBUG_BROADCASTS) {
@@ -21001,6 +21004,10 @@
                             removedPackage, extras, 0 /*flags*/,
                             installerPackageName, null, broadcastUsers, instantUserIds, null, null);
                 }
+                packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED_INTERNAL,
+                        removedPackage, extras, 0 /*flags*/, PLATFORM_PACKAGE_NAME,
+                        null /*finishedReceiver*/, broadcastUsers, instantUserIds,
+                        broadcastAllowList, null /*bOptions*/);
                 if (dataRemoved && !isRemovedPackageSystemUpdate) {
                     packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
                             removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null,