Broadcast PACKAGE_CHANGED in response to OVERLAY_CHANGED

Consolidate what triggers a package change: teach the package manager to
broadcast PACKAGE_CHANGED in response to the overlay manager
broadcasting OVERLAY_CHANGED. Since the overlay manager listens for
PACKAGE_CHANGED, the package manager will include a new extra in the
intent to act as a secret handshake between the managers that the
overlay manager should ignore the intent in order to prevent an endless
loop.

Bug: 140790688
Test: manual (adb shell cmd overlay enable ... && adb shell dumpsys activity broadcasts # check "Historical broadcasts")
Change-Id: I430efcce5adf0dca03058cd6ff694541f0ac3ef5
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 9e7b4648..5f3e503 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -17,11 +17,13 @@
 package com.android.server.om;
 
 import static android.app.AppGlobals.getPackageManager;
+import static android.content.Intent.ACTION_OVERLAY_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REASON;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 import static android.os.Trace.TRACE_TAG_RRO;
 import static android.os.Trace.traceBegin;
@@ -356,7 +358,11 @@
                     }
                     break;
                 case ACTION_PACKAGE_CHANGED:
-                    onPackageChanged(packageName, userIds);
+                    // ignore the intent if it was sent by the package manager as a result of the
+                    // overlay manager having sent ACTION_OVERLAY_CHANGED
+                    if (!ACTION_OVERLAY_CHANGED.equals(intent.getStringExtra(EXTRA_REASON))) {
+                        onPackageChanged(packageName, userIds);
+                    }
                     break;
                 case ACTION_PACKAGE_REMOVED:
                     if (replacing) {
@@ -885,7 +891,7 @@
             FgThread.getHandler().post(() -> {
                 updateAssets(userId, targetPackageName);
 
-                final Intent intent = new Intent(Intent.ACTION_OVERLAY_CHANGED,
+                final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
                         Uri.fromParts("package", targetPackageName, null));
                 intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
 
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b1d05f4..2d3be69 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1675,7 +1675,8 @@
                     }
                     // Send broadcasts
                     for (int i = 0; i < size; i++) {
-                        sendPackageChangedBroadcast(packages[i], true, components[i], uids[i]);
+                        sendPackageChangedBroadcast(packages[i], true, components[i], uids[i],
+                                null);
                     }
                     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                     break;
@@ -2163,7 +2164,7 @@
                     // send broadcast that all consumers of the static shared library have changed
                     sendPackageChangedBroadcast(pkg.packageName, false /*killFlag*/,
                             new ArrayList<>(Collections.singletonList(pkg.packageName)),
-                            pkg.applicationInfo.uid);
+                            pkg.applicationInfo.uid, null);
                 }
             }
 
@@ -20039,7 +20040,7 @@
             if (sendNow) {
                 int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                 sendPackageChangedBroadcast(packageName,
-                        (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
+                        (flags & PackageManager.DONT_KILL_APP) != 0, components, packageUid, null);
             }
         } finally {
             Binder.restoreCallingIdentity(callingId);
@@ -20071,7 +20072,8 @@
     }
 
     private void sendPackageChangedBroadcast(String packageName,
-            boolean killFlag, ArrayList<String> componentNames, int packageUid) {
+            boolean killFlag, ArrayList<String> componentNames, int packageUid,
+            String reason) {
         if (DEBUG_INSTALL)
             Log.v(TAG, "Sending package changed: package=" + packageName + " components="
                     + componentNames);
@@ -20082,6 +20084,9 @@
         extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
         extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
         extras.putInt(Intent.EXTRA_UID, packageUid);
+        if (reason != null) {
+            extras.putString(Intent.EXTRA_REASON, reason);
+        }
         // If this is not reporting a change of the overall package, then only send it
         // to registered receivers.  We don't want to launch a swath of apps for every
         // little component state change.
@@ -20329,6 +20334,34 @@
             }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
         }
 
+        IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
+        overlayFilter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent == null) {
+                    return;
+                }
+                Uri data = intent.getData();
+                if (data == null) {
+                    return;
+                }
+                String packageName = data.getSchemeSpecificPart();
+                if (packageName == null) {
+                    return;
+                }
+                PackageParser.Package pkg = mPackages.get(packageName);
+                if (pkg == null) {
+                    return;
+                }
+                sendPackageChangedBroadcast(pkg.packageName,
+                        false /* killFlag */,
+                        new ArrayList<>(Collections.singletonList(pkg.packageName)),
+                        pkg.applicationInfo.uid,
+                        Intent.ACTION_OVERLAY_CHANGED);
+            }
+        }, overlayFilter);
+
         mModuleInfoProvider.systemReady();
 
         // Installer service might attempt to install some packages that have been staged for