Merge changes from topic "oct11"

* changes:
  BroadcastQueue: Handle successor ProcessRecords.
  More robust ProcessRecord routing for resultTo.
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 81aa6da..b803070 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -420,10 +420,11 @@
 
     public abstract int broadcastIntentInPackage(String packageName, @Nullable String featureId,
             int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
-            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
-            String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            @UserIdInt int userId, boolean allowBackgroundActivityStarts,
-            @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList);
+            IApplicationThread resultToThread, IIntentReceiver resultTo, int resultCode,
+            String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
+            boolean serialized, boolean sticky, @UserIdInt int userId,
+            boolean allowBackgroundActivityStarts, @Nullable IBinder backgroundActivityStartsToken,
+            @Nullable int[] broadcastAllowList);
 
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 6404a1f..7475ef8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -556,7 +556,8 @@
     void startConfirmDeviceCredentialIntent(in Intent intent, in Bundle options);
     @UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
     void sendIdleJobTrigger();
-    int sendIntentSender(in IIntentSender target, in IBinder whitelistToken, int code,
+    int sendIntentSender(in IApplicationThread caller, in IIntentSender target,
+            in IBinder whitelistToken, int code,
             in Intent intent, in String resolvedType, in IIntentReceiver finishedReceiver,
             in String requiredPermission, in Bundle options);
     boolean isBackgroundRestricted(in String packageName);
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index bc78df5..db47a4c 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -1009,7 +1009,9 @@
                 options = activityOptions.toBundle();
             }
 
-            return ActivityManager.getService().sendIntentSender(
+            final IApplicationThread app = ActivityThread.currentActivityThread()
+                    .getApplicationThread();
+            return ActivityManager.getService().sendIntentSender(app,
                     mTarget, mWhitelistToken, code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java
index b1252fd..8853b70 100644
--- a/core/java/android/content/IntentSender.java
+++ b/core/java/android/content/IntentSender.java
@@ -18,6 +18,8 @@
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.IApplicationThread;
 import android.app.ActivityManager.PendingIntentInfo;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Bundle;
@@ -194,7 +196,9 @@
             String resolvedType = intent != null ?
                     intent.resolveTypeIfNeeded(context.getContentResolver())
                     : null;
-            int res = ActivityManager.getService().sendIntentSender(mTarget, mWhitelistToken,
+            final IApplicationThread app = ActivityThread.currentActivityThread()
+                    .getApplicationThread();
+            int res = ActivityManager.getService().sendIntentSender(app, mTarget, mWhitelistToken,
                     code, intent, resolvedType,
                     onFinished != null
                             ? new FinishedDispatcher(this, onFinished, handler)
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1a4da7d..7a09109 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3902,12 +3902,13 @@
                     if (isInstantApp) {
                         intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, 0, null, null, permission.ACCESS_INSTANT_APPS, null,
-                                false, false, resolvedUserId, false, null, visibilityAllowList);
+                                null, null, null, 0, null, null, permission.ACCESS_INSTANT_APPS,
+                                null, false, false, resolvedUserId, false, null,
+                                visibilityAllowList);
                     } else {
                         broadcastIntentInPackage("android", null, SYSTEM_UID, uid, pid, intent,
-                                null, null, 0, null, null, null, null, false, false, resolvedUserId,
-                                false, null, visibilityAllowList);
+                                null, null, null, 0, null, null, null, null, false, false,
+                                resolvedUserId, false, null, visibilityAllowList);
                     }
 
                     if (observer != null) {
@@ -4444,7 +4445,8 @@
         intent.putExtra(Intent.EXTRA_UID, uid);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
         broadcastIntentLocked(null /* callerApp */, null /* callerPackage */,
-                null /* callerFeatureId */, intent, null /* resolvedType */, null /* resultTo */,
+                null /* callerFeatureId */, intent, null /* resolvedType */,
+                null /* resultToApp */, null /* resultTo */,
                 0 /* resultCode */, null /* resultData */, null /* resultExtras */,
                 null /* requiredPermissions */, null /* excludedPermissions */,
                 null /* excludedPackages */, OP_NONE, null /* bOptions */, false /* ordered */,
@@ -5515,12 +5517,12 @@
     }
 
     @Override
-    public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
-            Intent intent, String resolvedType,
+    public int sendIntentSender(IApplicationThread caller, IIntentSender target,
+            IBinder allowlistToken, int code, Intent intent, String resolvedType,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
         if (target instanceof PendingIntentRecord) {
-            return ((PendingIntentRecord)target).sendWithResult(code, intent, resolvedType,
-                    allowlistToken, finishedReceiver, requiredPermission, options);
+            return ((PendingIntentRecord) target).sendWithResult(caller, code, intent,
+                    resolvedType, allowlistToken, finishedReceiver, requiredPermission, options);
         } else {
             if (intent == null) {
                 // Weird case: someone has given us their own custom IIntentSender, and now
@@ -13586,8 +13588,8 @@
                     BroadcastQueue queue = broadcastQueueForIntent(intent);
                     BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                             null, null, -1, -1, false, null, null, null, null, OP_NONE, null,
-                            receivers, null, 0, null, null, false, true, true, -1, false, null,
-                            false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
+                            receivers, null, null, 0, null, null, false, true, true, -1, false,
+                            null, false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */,
                             null /* filterExtrasForReceiver */);
                     queue.enqueueBroadcastLocked(r);
                 }
@@ -13842,9 +13844,9 @@
             boolean sticky, int callingPid,
             int callingUid, int realCallingUid, int realCallingPid, int userId) {
         return broadcastIntentLocked(callerApp, callerPackage, callerFeatureId, intent,
-                resolvedType, resultTo, resultCode, resultData, resultExtras, requiredPermissions,
-                excludedPermissions, excludedPackages, appOp, bOptions, ordered, sticky, callingPid,
-                callingUid, realCallingUid, realCallingPid, userId,
+                resolvedType, null, resultTo, resultCode, resultData, resultExtras,
+                requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
+                ordered, sticky, callingPid, callingUid, realCallingUid, realCallingPid, userId,
                 false /* allowBackgroundActivityStarts */,
                 null /* tokenNeededForBackgroundActivityStarts */,
                 null /* broadcastAllowList */, null /* filterExtrasForReceiver */);
@@ -13853,7 +13855,7 @@
     @GuardedBy("this")
     final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
             @Nullable String callerFeatureId, Intent intent, String resolvedType,
-            IIntentReceiver resultTo, int resultCode, String resultData,
+            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode, String resultData,
             Bundle resultExtras, String[] requiredPermissions,
             String[] excludedPermissions, String[] excludedPackages, int appOp, Bundle bOptions,
             boolean ordered, boolean sticky, int callingPid, int callingUid,
@@ -13862,6 +13864,18 @@
             @Nullable IBinder backgroundActivityStartsToken,
             @Nullable int[] broadcastAllowList,
             @Nullable BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver) {
+        if ((resultTo != null) && (resultToApp == null)) {
+            if (resultTo.asBinder() instanceof BinderProxy) {
+                // Warn when requesting results without a way to deliver them
+                Slog.wtf(TAG, "Sending broadcast " + intent.getAction()
+                        + " with resultTo requires resultToApp", new Throwable());
+            } else {
+                // If not a BinderProxy above, then resultTo is an in-process
+                // receiver, so splice in system_server process
+                resultToApp = getProcessRecordLocked("system", SYSTEM_UID);
+            }
+        }
+
         intent = new Intent(intent);
 
         final boolean callerInstantApp = isInstantApp(callerApp, callerPackage, callingUid);
@@ -14461,8 +14475,8 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
-                    registeredReceivers, resultTo, resultCode, resultData, resultExtras, ordered,
-                    sticky, false, userId, allowBackgroundActivityStarts,
+                    registeredReceivers, resultToApp, resultTo, resultCode, resultData,
+                    resultExtras, ordered, sticky, false, userId, allowBackgroundActivityStarts,
                     backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
             if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
             queue.enqueueBroadcastLocked(r);
@@ -14555,7 +14569,7 @@
             BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
-                    receivers, resultTo, resultCode, resultData, resultExtras,
+                    receivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
                     ordered, sticky, false, userId, allowBackgroundActivityStarts,
                     backgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
 
@@ -14680,6 +14694,9 @@
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
 
+            // We're delivering the result to the caller
+            final ProcessRecord resultToApp = callerApp;
+
             // Non-system callers can't declare that a broadcast is alarm-related.
             // The PendingIntent invocation case is handled in PendingIntentRecord.
             if (bOptions != null && callingUid != SYSTEM_UID) {
@@ -14697,9 +14714,10 @@
             try {
                 return broadcastIntentLocked(callerApp,
                         callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
-                        intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
-                        requiredPermissions, excludedPermissions, excludedPackages, appOp, bOptions,
-                        serialized, sticky, callingPid, callingUid, callingUid, callingPid, userId);
+                        intent, resolvedType, resultToApp, resultTo, resultCode, resultData,
+                        resultExtras, requiredPermissions, excludedPermissions, excludedPackages,
+                        appOp, bOptions, serialized, sticky, callingPid, callingUid, callingUid,
+                        callingPid, userId, false, null, null, null);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
@@ -14709,11 +14727,10 @@
     // Not the binder call surface
     int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
             int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
-            IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
-            String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-            int userId, boolean allowBackgroundActivityStarts,
-            @Nullable IBinder backgroundActivityStartsToken,
-            @Nullable int[] broadcastAllowList) {
+            ProcessRecord resultToApp, IIntentReceiver resultTo, int resultCode,
+            String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
+            boolean serialized, boolean sticky, int userId, boolean allowBackgroundActivityStarts,
+            @Nullable IBinder backgroundActivityStartsToken, @Nullable int[] broadcastAllowList) {
         synchronized(this) {
             intent = verifyBroadcastLocked(intent);
 
@@ -14722,9 +14739,9 @@
                     : new String[] {requiredPermission};
             try {
                 return broadcastIntentLocked(null, packageName, featureId, intent, resolvedType,
-                        resultTo, resultCode, resultData, resultExtras, requiredPermissions, null,
-                        null, OP_NONE, bOptions, serialized, sticky, -1, uid, realCallingUid,
-                        realCallingPid, userId, allowBackgroundActivityStarts,
+                        resultToApp, resultTo, resultCode, resultData, resultExtras,
+                        requiredPermissions, null, null, OP_NONE, bOptions, serialized, sticky, -1,
+                        uid, realCallingUid, realCallingPid, userId, allowBackgroundActivityStarts,
                         backgroundActivityStartsToken, broadcastAllowList,
                         null /* filterExtrasForReceiver */);
             } finally {
@@ -17253,16 +17270,18 @@
         @Override
         public int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid,
                 int realCallingUid, int realCallingPid, Intent intent, String resolvedType,
-                IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras,
-                String requiredPermission, Bundle bOptions, boolean serialized, boolean sticky,
-                int userId, boolean allowBackgroundActivityStarts,
+                IApplicationThread resultToThread, IIntentReceiver resultTo, int resultCode,
+                String resultData, Bundle resultExtras, String requiredPermission, Bundle bOptions,
+                boolean serialized, boolean sticky, int userId,
+                boolean allowBackgroundActivityStarts,
                 @Nullable IBinder backgroundActivityStartsToken,
                 @Nullable int[] broadcastAllowList) {
             synchronized (ActivityManagerService.this) {
+                final ProcessRecord resultToApp = getRecordForAppLOSP(resultToThread);
                 return ActivityManagerService.this.broadcastIntentInPackage(packageName, featureId,
-                        uid, realCallingUid, realCallingPid, intent, resolvedType, resultTo,
-                        resultCode, resultData, resultExtras, requiredPermission, bOptions,
-                        serialized, sticky, userId, allowBackgroundActivityStarts,
+                        uid, realCallingUid, realCallingPid, intent, resolvedType, resultToApp,
+                        resultTo, resultCode, resultData, resultExtras, requiredPermission,
+                        bOptions, serialized, sticky, userId, allowBackgroundActivityStarts,
                         backgroundActivityStartsToken, broadcastAllowList);
             }
         }
@@ -17283,8 +17302,9 @@
                 try {
                     return ActivityManagerService.this.broadcastIntentLocked(null /*callerApp*/,
                             null /*callerPackage*/, null /*callingFeatureId*/, intent,
-                            null /*resolvedType*/, resultTo, 0 /*resultCode*/, null /*resultData*/,
-                            null /*resultExtras*/, requiredPermissions,
+                            null /* resolvedType */, null /* resultToApp */, resultTo,
+                            0 /* resultCode */, null /* resultData */,
+                            null /* resultExtras */, requiredPermissions,
                             null /*excludedPermissions*/, null /*excludedPackages*/,
                             AppOpsManager.OP_NONE, bOptions /*options*/, serialized,
                             false /*sticky*/, callingPid, callingUid, callingUid, callingPid,
@@ -17897,7 +17917,7 @@
         public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code,
                 Intent intent, String resolvedType,
                 IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-            return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code,
+            return ActivityManagerService.this.sendIntentSender(null, target, allowlistToken, code,
                     intent, resolvedType, finishedReceiver, requiredPermission, options);
         }
 
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 97635b5..0d6ac1d 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -114,6 +114,14 @@
     private int mActiveIndex;
 
     /**
+     * When defined, the receiver actively being dispatched into this process
+     * was considered "blocked" until at least the given count of other
+     * receivers have reached a terminal state; typically used for ordered
+     * broadcasts and priority traunches.
+     */
+    private int mActiveBlockedUntilTerminalCount;
+
+    /**
      * Count of {@link #mActive} broadcasts that have been dispatched since this
      * queue was last idle.
      */
@@ -304,6 +312,7 @@
         final SomeArgs next = mPending.removeFirst();
         mActive = (BroadcastRecord) next.arg1;
         mActiveIndex = next.argi1;
+        mActiveBlockedUntilTerminalCount = next.argi2;
         mActiveCountSinceIdle++;
         mActiveViaColdStart = false;
         next.recycle();
@@ -316,6 +325,7 @@
     public void makeActiveIdle() {
         mActive = null;
         mActiveIndex = 0;
+        mActiveBlockedUntilTerminalCount = -1;
         mActiveCountSinceIdle = 0;
         mActiveViaColdStart = false;
         invalidateRunnableAt();
@@ -664,27 +674,14 @@
         }
         pw.print(" because ");
         pw.print(reasonToString(mRunnableAtReason));
-        if (mRunnableAtReason == REASON_BLOCKED) {
-            final SomeArgs next = mPending.peekFirst();
-            if (next != null) {
-                final BroadcastRecord r = (BroadcastRecord) next.arg1;
-                final int blockedUntilTerminalCount = next.argi2;
-                pw.print(" waiting for ");
-                pw.print(blockedUntilTerminalCount);
-                pw.print(" at ");
-                pw.print(r.terminalCount);
-                pw.print(" of ");
-                pw.print(r.receivers.size());
-            }
-        }
         pw.println();
         pw.increaseIndent();
         if (mActive != null) {
-            dumpRecord(now, pw, mActive, mActiveIndex);
+            dumpRecord(now, pw, mActive, mActiveIndex, mActiveBlockedUntilTerminalCount);
         }
         for (SomeArgs args : mPending) {
             final BroadcastRecord r = (BroadcastRecord) args.arg1;
-            dumpRecord(now, pw, r, args.argi1);
+            dumpRecord(now, pw, r, args.argi1, args.argi2);
         }
         pw.decreaseIndent();
         pw.println();
@@ -692,7 +689,7 @@
 
     @NeverCompile
     private void dumpRecord(@UptimeMillisLong long now, @NonNull IndentingPrintWriter pw,
-            @NonNull BroadcastRecord record, int recordIndex) {
+            @NonNull BroadcastRecord record, int recordIndex, int blockedUntilTerminalCount) {
         TimeUtils.formatDuration(record.enqueueTime, now, pw);
         pw.print(' ');
         pw.println(record.toShortString());
@@ -714,5 +711,13 @@
             pw.print(info.activityInfo.name);
         }
         pw.println();
+        if (blockedUntilTerminalCount != -1) {
+            pw.print("    blocked until ");
+            pw.print(blockedUntilTerminalCount);
+            pw.print(", currently at ");
+            pw.print(record.terminalCount);
+            pw.print(" of ");
+            pw.println(record.receivers.size());
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index d7a075b..f34565b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -262,7 +262,7 @@
                 if (oldRecord.resultTo != null) {
                     try {
                         oldRecord.mIsReceiverAppRunning = true;
-                        performReceiveLocked(oldRecord.callerApp, oldRecord.resultTo,
+                        performReceiveLocked(oldRecord.resultToApp, oldRecord.resultTo,
                                 oldRecord.intent,
                                 Activity.RESULT_CANCELED, null, null,
                                 false, false, oldRecord.userId, oldRecord.callingUid, r.callingUid,
@@ -1120,7 +1120,7 @@
                                 r.dispatchTime = now;
                             }
                             r.mIsReceiverAppRunning = true;
-                            performReceiveLocked(r.callerApp, r.resultTo,
+                            performReceiveLocked(r.resultToApp, r.resultTo,
                                     new Intent(r.intent), r.resultCode,
                                     r.resultData, r.resultExtras, false, false, r.userId,
                                     r.callingUid, r.callingUid,
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 89a0283..1e1ebeb 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -396,17 +396,12 @@
             // Emit all trace events for this process into a consistent track
             queue.traceTrackName = TAG + ".mRunning[" + queueIndex + "]";
 
-            // If we're already warm, boost OOM adjust now; if cold we'll boost
-            // it after the app has been started
-            if (processWarm) {
-                notifyStartedRunning(queue);
-            }
-
             // If we're already warm, schedule next pending broadcast now;
             // otherwise we'll wait for the cold start to circle back around
             queue.makeActiveNextPending();
             if (processWarm) {
                 queue.traceProcessRunningBegin();
+                notifyStartedRunning(queue);
                 scheduleReceiverWarmLocked(queue);
             } else {
                 queue.traceProcessStartingBegin();
@@ -441,15 +436,22 @@
 
     @Override
     public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app) {
+        // Process records can be recycled, so always start by looking up the
+        // relevant per-process queue
+        final BroadcastProcessQueue queue = getProcessQueue(app);
+        if (queue != null) {
+            queue.app = app;
+        }
+
         boolean didSomething = false;
-        if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
+        if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
             // We've been waiting for this app to cold start, and it's ready
             // now; dispatch its next broadcast and clear the slot
-            final BroadcastProcessQueue queue = mRunningColdStart;
             mRunningColdStart = null;
 
             queue.traceProcessEnd();
             queue.traceProcessRunningBegin();
+            notifyStartedRunning(queue);
             scheduleReceiverWarmLocked(queue);
 
             // We might be willing to kick off another cold start
@@ -471,19 +473,25 @@
 
     @Override
     public void onApplicationCleanupLocked(@NonNull ProcessRecord app) {
-        if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
+        // Process records can be recycled, so always start by looking up the
+        // relevant per-process queue
+        final BroadcastProcessQueue queue = getProcessQueue(app);
+        if (queue != null) {
+            queue.app = null;
+        }
+
+        if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
             // We've been waiting for this app to cold start, and it had
             // trouble; clear the slot and fail delivery below
             mRunningColdStart = null;
 
+            queue.traceProcessEnd();
+
             // We might be willing to kick off another cold start
             enqueueUpdateRunningList();
         }
 
-        final BroadcastProcessQueue queue = getProcessQueue(app);
         if (queue != null) {
-            queue.app = null;
-
             // If queue was running a broadcast, fail it
             if (queue.isActive()) {
                 finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
@@ -567,7 +575,7 @@
                 }
             } else {
                 // Otherwise we don't need to block at all
-                blockedUntilTerminalCount = 0;
+                blockedUntilTerminalCount = -1;
             }
 
             queue.enqueueOrReplaceBroadcast(r, i, blockedUntilTerminalCount);
@@ -619,9 +627,7 @@
         if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue);
         queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags,
                 hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
-        if (queue.app != null) {
-            notifyStartedRunning(queue);
-        } else {
+        if (queue.app == null) {
             mRunningColdStart = null;
             finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
             return;
@@ -749,8 +755,8 @@
      * ordered broadcast; assumes the sender is still a warm process.
      */
     private void scheduleResultTo(@NonNull BroadcastRecord r) {
-        if ((r.callerApp == null) || (r.resultTo == null)) return;
-        final ProcessRecord app = r.callerApp;
+        if ((r.resultToApp == null) || (r.resultTo == null)) return;
+        final ProcessRecord app = r.resultToApp;
         final IApplicationThread thread = app.getThread();
         if (thread != null) {
             mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
@@ -1159,6 +1165,12 @@
                 }
             }
 
+            // Verify that pending cold start hasn't been orphaned
+            if (mRunningColdStart != null) {
+                checkState(getRunningIndexOf(mRunningColdStart) >= 0,
+                        "isOrphaned " + mRunningColdStart);
+            }
+
             // Verify health of all known process queues
             for (int i = 0; i < mProcessQueues.size(); i++) {
                 BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index bcc76e9..2d82595 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -94,6 +94,7 @@
     final @Nullable BroadcastOptions options; // BroadcastOptions supplied by caller
     final @NonNull List<Object> receivers;   // contains BroadcastFilter and ResolveInfo
     final @DeliveryState int[] delivery;   // delivery state of each receiver
+    @Nullable ProcessRecord resultToApp; // who receives final result if non-null
     @Nullable IIntentReceiver resultTo; // who receives final result if non-null
     boolean deferred;
     int splitCount;         // refcount for result callback, when split
@@ -345,7 +346,8 @@
             boolean _callerInstantApp, String _resolvedType,
             String[] _requiredPermissions, String[] _excludedPermissions,
             String[] _excludedPackages, int _appOp,
-            BroadcastOptions _options, List _receivers, IIntentReceiver _resultTo, int _resultCode,
+            BroadcastOptions _options, List _receivers,
+            ProcessRecord _resultToApp, IIntentReceiver _resultTo, int _resultCode,
             String _resultData, Bundle _resultExtras, boolean _serialized, boolean _sticky,
             boolean _initialSticky, int _userId, boolean allowBackgroundActivityStarts,
             @Nullable IBinder backgroundActivityStartsToken, boolean timeoutExempt,
@@ -372,6 +374,7 @@
         delivery = new int[_receivers != null ? _receivers.size() : 0];
         scheduledTime = new long[delivery.length];
         terminalTime = new long[delivery.length];
+        resultToApp = _resultToApp;
         resultTo = _resultTo;
         resultCode = _resultCode;
         resultData = _resultData;
@@ -421,6 +424,7 @@
         delivery = from.delivery;
         scheduledTime = from.scheduledTime;
         terminalTime = from.terminalTime;
+        resultToApp = from.resultToApp;
         resultTo = from.resultTo;
         enqueueTime = from.enqueueTime;
         enqueueRealTime = from.enqueueRealTime;
@@ -480,8 +484,8 @@
         BroadcastRecord split = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                 callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                 requiredPermissions, excludedPermissions, excludedPackages, appOp, options,
-                splitReceivers, resultTo, resultCode, resultData, resultExtras, ordered, sticky,
-                initialSticky, userId, allowBackgroundActivityStarts,
+                splitReceivers, resultToApp, resultTo, resultCode, resultData, resultExtras,
+                ordered, sticky, initialSticky, userId, allowBackgroundActivityStarts,
                 mBackgroundActivityStartsToken, timeoutExempt, filterExtrasForReceiver);
         split.enqueueTime = this.enqueueTime;
         split.enqueueRealTime = this.enqueueRealTime;
@@ -559,7 +563,7 @@
             final BroadcastRecord br = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                     callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                     requiredPermissions, excludedPermissions, excludedPackages, appOp, options,
-                    uid2receiverList.valueAt(i), null /* _resultTo */,
+                    uid2receiverList.valueAt(i), null /* _resultToApp */, null /* _resultTo */,
                     resultCode, resultData, resultExtras, ordered, sticky, initialSticky, userId,
                     allowBackgroundActivityStarts, mBackgroundActivityStartsToken, timeoutExempt,
                     filterExtrasForReceiver);
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index bda60ff..975619f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -26,6 +26,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.BroadcastOptions;
+import android.app.IApplicationThread;
 import android.app.PendingIntent;
 import android.content.IIntentReceiver;
 import android.content.IIntentSender;
@@ -302,13 +303,21 @@
 
     public void send(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver,
+        sendInner(null, code, intent, resolvedType, allowlistToken, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options);
     }
 
-    public int sendWithResult(int code, Intent intent, String resolvedType, IBinder allowlistToken,
-            IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
-        return sendInner(code, intent, resolvedType, allowlistToken, finishedReceiver,
+    public void send(IApplicationThread caller, int code, Intent intent, String resolvedType,
+            IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission,
+            Bundle options) {
+        sendInner(caller, code, intent, resolvedType, allowlistToken, finishedReceiver,
+                requiredPermission, null, null, 0, 0, 0, options);
+    }
+
+    public int sendWithResult(IApplicationThread caller, int code, Intent intent,
+            String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver,
+            String requiredPermission, Bundle options) {
+        return sendInner(caller, code, intent, resolvedType, allowlistToken, finishedReceiver,
                 requiredPermission, null, null, 0, 0, 0, options);
     }
 
@@ -339,9 +348,19 @@
                 ActivityOptions.PENDING_INTENT_BAL_ALLOWED_DEFAULT);
     }
 
+    @Deprecated
     public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken,
             IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo,
             String resultWho, int requestCode, int flagsMask, int flagsValues, Bundle options) {
+        return sendInner(null, code, intent, resolvedType, allowlistToken, finishedReceiver,
+                requiredPermission, resultTo, resultWho, requestCode, flagsMask, flagsValues,
+                options);
+    }
+
+    public int sendInner(IApplicationThread caller, int code, Intent intent,
+            String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver,
+            String requiredPermission, IBinder resultTo, String resultWho, int requestCode,
+            int flagsMask, int flagsValues, Bundle options) {
         if (intent != null) intent.setDefusable(true);
         if (options != null) options.setDefusable(true);
 
@@ -468,6 +487,7 @@
                 }
             }
 
+            final IApplicationThread finishedReceiverThread = caller;
             boolean sendFinish = finishedReceiver != null;
             int userId = key.userId;
             if (userId == UserHandle.USER_CURRENT) {
@@ -525,9 +545,9 @@
                         // that the broadcast be delivered synchronously
                         int sent = controller.mAmInternal.broadcastIntentInPackage(key.packageName,
                                 key.featureId, uid, callingUid, callingPid, finalIntent,
-                                resolvedType, finishedReceiver, code, null, null,
-                                requiredPermission, options, (finishedReceiver != null), false,
-                                userId, allowedByToken || allowTrampoline, bgStartsToken,
+                                resolvedType, finishedReceiverThread, finishedReceiver, code, null,
+                                null, requiredPermission, options, (finishedReceiver != null),
+                                false, userId, allowedByToken || allowTrampoline, bgStartsToken,
                                 null /* broadcastAllowList */);
                         if (sent == ActivityManager.BROADCAST_SUCCESS) {
                             sendFinish = false;
diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java
index 9b7c3ac..77fcef6 100644
--- a/services/core/java/com/android/server/am/PreBootBroadcaster.java
+++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java
@@ -57,7 +57,6 @@
     private static final String TAG = "PreBootBroadcaster";
 
     private final ActivityManagerService mService;
-    private final ProcessRecord mSystemApp;
     private final int mUserId;
     private final ProgressReporter mProgress;
     private final boolean mQuiet;
@@ -70,9 +69,6 @@
     public PreBootBroadcaster(ActivityManagerService service, int userId,
             ProgressReporter progress, boolean quiet) {
         mService = service;
-        synchronized (mService) {
-            mSystemApp = mService.getProcessRecordLocked("system", android.os.Process.SYSTEM_UID);
-        }
         mUserId = userId;
         mProgress = progress;
         mQuiet = quiet;
@@ -127,7 +123,7 @@
                 TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
                 REASON_PRE_BOOT_COMPLETED, "");
         synchronized (mService) {
-            mService.broadcastIntentLocked(mSystemApp, "android", null, mIntent, null, this, 0,
+            mService.broadcastIntentLocked(null, null, null, mIntent, null, this, 0,
                     null, null, null, null, null, AppOpsManager.OP_NONE, bOptions.toBundle(), true,
                     false, ActivityManagerService.MY_PID,
                     Process.SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), mUserId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 86915da..90b1f4e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -140,7 +140,7 @@
     private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
             List receivers, boolean ordered) {
         return new BroadcastRecord(mImpl, intent, mProcess, PACKAGE_RED, null, 21, 42, false, null,
-                null, null, null, AppOpsManager.OP_NONE, options, receivers, null,
+                null, null, null, AppOpsManager.OP_NONE, options, receivers, null, null,
                 Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
                 false, null, false, null);
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 076fce9..c125448 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -113,6 +113,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.UnaryOperator;
 
 /**
@@ -154,10 +155,11 @@
     private BroadcastQueue mQueue;
 
     /**
-     * When enabled {@link ActivityManagerService#startProcessLocked} will fail
-     * by returning {@code null}; otherwise it will spawn a new mock process.
+     * Desired behavior of the next
+     * {@link ActivityManagerService#startProcessLocked} call.
      */
-    private boolean mFailStartProcess;
+    private AtomicReference<ProcessStartBehavior> mNextProcessStartBehavior = new AtomicReference<>(
+            ProcessStartBehavior.SUCCESS);
 
     /**
      * Map from PID to registered registered runtime receivers.
@@ -216,16 +218,46 @@
         doAnswer((invocation) -> {
             Log.v(TAG, "Intercepting startProcessLocked() for "
                     + Arrays.toString(invocation.getArguments()));
-            if (mFailStartProcess) {
+            final ProcessStartBehavior behavior = mNextProcessStartBehavior
+                    .getAndSet(ProcessStartBehavior.SUCCESS);
+            if (behavior == ProcessStartBehavior.FAIL_NULL) {
                 return null;
             }
             final String processName = invocation.getArgument(0);
             final ApplicationInfo ai = invocation.getArgument(1);
             final ProcessRecord res = makeActiveProcessRecord(ai, processName,
                     ProcessBehavior.NORMAL, UnaryOperator.identity());
+            final ProcessRecord deliverRes;
+            switch (behavior) {
+                case SUCCESS_PREDECESSOR:
+                case FAIL_TIMEOUT_PREDECESSOR:
+                    // Create a different process that will be linked to the
+                    // returned process via a predecessor/successor relationship
+                    mActiveProcesses.remove(res);
+                    deliverRes = makeActiveProcessRecord(ai, processName,
+                          ProcessBehavior.NORMAL, UnaryOperator.identity());
+                    deliverRes.mPredecessor = res;
+                    res.mSuccessor = deliverRes;
+                    break;
+                default:
+                    deliverRes = res;
+                    break;
+            }
             mHandlerThread.getThreadHandler().post(() -> {
                 synchronized (mAms) {
-                    mQueue.onApplicationAttachedLocked(res);
+                    switch (behavior) {
+                        case SUCCESS:
+                        case SUCCESS_PREDECESSOR:
+                            mQueue.onApplicationAttachedLocked(deliverRes);
+                            break;
+                        case FAIL_TIMEOUT:
+                        case FAIL_TIMEOUT_PREDECESSOR:
+                            mActiveProcesses.remove(deliverRes);
+                            mQueue.onApplicationTimeoutLocked(deliverRes);
+                            break;
+                        default:
+                            throw new UnsupportedOperationException();
+                    }
                 }
             });
             return res;
@@ -281,9 +313,10 @@
 
         // Verify that all processes have finished handling broadcasts
         for (ProcessRecord app : mActiveProcesses) {
-            assertTrue(app.toShortString(), app.mReceivers.numberOfCurReceivers() == 0);
-            assertTrue(app.toShortString(), mQueue.getPreferredSchedulingGroupLocked(app)
-                    == ProcessList.SCHED_GROUP_UNDEFINED);
+            assertEquals(app.toShortString(), 0,
+                    app.mReceivers.numberOfCurReceivers());
+            assertEquals(app.toShortString(), ProcessList.SCHED_GROUP_UNDEFINED,
+                    mQueue.getPreferredSchedulingGroupLocked(app));
         }
     }
 
@@ -325,6 +358,19 @@
         }
     }
 
+    private enum ProcessStartBehavior {
+        /** Process starts successfully */
+        SUCCESS,
+        /** Process starts successfully via predecessor */
+        SUCCESS_PREDECESSOR,
+        /** Process fails by reporting timeout */
+        FAIL_TIMEOUT,
+        /** Process fails by reporting timeout via predecessor */
+        FAIL_TIMEOUT_PREDECESSOR,
+        /** Process fails by immediately returning null */
+        FAIL_NULL,
+    }
+
     private enum ProcessBehavior {
         /** Process broadcasts normally */
         NORMAL,
@@ -520,8 +566,8 @@
             IIntentReceiver orderedResultTo, Bundle orderedExtras, int userId) {
         return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null,
                 callerApp.getPid(), callerApp.info.uid, false, null, null, null, null,
-                AppOpsManager.OP_NONE, options, receivers, orderedResultTo, Activity.RESULT_OK,
-                null, orderedExtras, ordered, false, false, userId, false, null,
+                AppOpsManager.OP_NONE, options, receivers, callerApp, orderedResultTo,
+                Activity.RESULT_OK, null, orderedExtras, ordered, false, false, userId, false, null,
                 false, null);
     }
 
@@ -956,18 +1002,16 @@
         final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
 
         // Send broadcast while process starts are failing
-        mFailStartProcess = true;
+        mNextProcessStartBehavior.set(ProcessStartBehavior.FAIL_NULL);
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
-                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
-                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
 
         // Confirm that queue goes idle, with no processes
         waitForIdle();
         assertEquals(1, mActiveProcesses.size());
 
         // Send more broadcasts with working process starts
-        mFailStartProcess = false;
         final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
         enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
                 List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
@@ -981,7 +1025,6 @@
         final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
                 getUidForPackage(PACKAGE_YELLOW));
         verifyScheduleReceiver(never(), receiverGreenApp, airplane);
-        verifyScheduleReceiver(never(), receiverYellowApp, airplane);
         verifyScheduleReceiver(times(1), receiverGreenApp, timezone);
         verifyScheduleReceiver(times(1), receiverYellowApp, timezone);
     }
@@ -1071,6 +1114,52 @@
                 new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
     }
 
+    @Test
+    public void testCold_Success() throws Exception {
+        doCold(ProcessStartBehavior.SUCCESS);
+    }
+
+    @Test
+    public void testCold_Success_Predecessor() throws Exception {
+        doCold(ProcessStartBehavior.SUCCESS_PREDECESSOR);
+    }
+
+    @Test
+    public void testCold_Fail_Null() throws Exception {
+        doCold(ProcessStartBehavior.FAIL_NULL);
+    }
+
+    @Test
+    public void testCold_Fail_Timeout() throws Exception {
+        doCold(ProcessStartBehavior.FAIL_TIMEOUT);
+    }
+
+    @Test
+    public void testCold_Fail_Timeout_Predecessor() throws Exception {
+        doCold(ProcessStartBehavior.FAIL_TIMEOUT_PREDECESSOR);
+    }
+
+    private void doCold(ProcessStartBehavior behavior) throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+        mNextProcessStartBehavior.set(behavior);
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+        waitForIdle();
+
+        // Regardless of success/failure of above, we should always be able to
+        // recover and begin sending future broadcasts
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+        waitForIdle();
+
+        final ProcessRecord receiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+                getUidForPackage(PACKAGE_GREEN));
+        verifyScheduleReceiver(receiverApp, timezone);
+    }
+
     /**
      * Verify that we skip broadcasts to an app being backed up.
      */
@@ -1277,8 +1366,8 @@
         final BroadcastRecord r = new BroadcastRecord(mQueue, intent, callerApp,
                 callerApp.info.packageName, null, callerApp.getPid(), callerApp.info.uid, false,
                 null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(),
-                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, Activity.RESULT_OK,
-                null, null, false, false, false, UserHandle.USER_SYSTEM, true,
+                List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, null,
+                Activity.RESULT_OK, null, null, false, false, false, UserHandle.USER_SYSTEM, true,
                 backgroundActivityStartsToken, false, null);
         enqueueBroadcast(r);
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
index 161dfa0..11573c5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastRecordTest.java
@@ -525,6 +525,7 @@
                 0 /* appOp */,
                 null /* options */,
                 new ArrayList<>(receivers), // Make a copy to not affect the original list.
+                null /* resultToApp */,
                 null /* resultTo */,
                 0 /* resultCode */,
                 null /* resultData */,