Merge "Defer outgoing bcasts from processes in freezable state." into main
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index 75cfba0..d31baf3 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -144,6 +144,7 @@
     }
     repeated BroadcastSummary historical_broadcasts_summary = 6;
     repeated BroadcastRecordProto pending_broadcasts = 7;
+    repeated BroadcastRecordProto frozen_broadcasts = 8;
 }
 
 message MemInfoDumpProto {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 45f657d..4ebabdc 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1164,7 +1164,8 @@
         synchronized (mInternal) {
             synchronized (mInternal.mProcLock) {
                 app.mOptRecord.setFreezeSticky(isSticky);
-                mInternal.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncInternalLSP(app, 0, true);
+                mInternal.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncInternalLSP(
+                        app, 0 /* delayMillis */, true /* force */, false /* immediate */);
             }
         }
         return 0;
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 2fff79b..887915d 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -296,11 +296,21 @@
      * For {@link BroadcastQueueModernImpl}: How frequently we should check for the pending
      * cold start validity.
      */
-    public long PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 30 * 1000;
+    public long PENDING_COLD_START_CHECK_INTERVAL_MILLIS =
+            DEFAULT_PENDING_COLD_START_CHECK_INTERVAL_MILLIS;
     private static final String KEY_PENDING_COLD_START_CHECK_INTERVAL_MILLIS =
             "pending_cold_start_check_interval_millis";
     private static final long DEFAULT_PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 30_000;
 
+    /**
+     * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a
+     * freezable process that will be allowed before killing the process.
+     */
+    public long MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS;
+    private static final String KEY_MAX_FROZEN_OUTGOING_BROADCASTS =
+            "max_frozen_outgoing_broadcasts";
+    private static final int DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS = 32;
+
     // Settings override tracking for this instance
     private String mSettingsKey;
     private SettingsObserver mSettingsObserver;
@@ -453,6 +463,9 @@
             PENDING_COLD_START_CHECK_INTERVAL_MILLIS = getDeviceConfigLong(
                     KEY_PENDING_COLD_START_CHECK_INTERVAL_MILLIS,
                     DEFAULT_PENDING_COLD_START_CHECK_INTERVAL_MILLIS);
+            MAX_FROZEN_OUTGOING_BROADCASTS = getDeviceConfigInt(
+                    KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
+                    DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS);
         }
 
         // TODO: migrate BroadcastRecord to accept a BroadcastConstants
@@ -513,6 +526,8 @@
                     CORE_DEFER_UNTIL_ACTIVE).println();
             pw.print(KEY_PENDING_COLD_START_CHECK_INTERVAL_MILLIS,
                     PENDING_COLD_START_CHECK_INTERVAL_MILLIS).println();
+            pw.print(KEY_MAX_FROZEN_OUTGOING_BROADCASTS,
+                    MAX_FROZEN_OUTGOING_BROADCASTS).println();
             pw.decreaseIndent();
             pw.println();
         }
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index 34658ca..d6e3d43 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -50,6 +50,11 @@
     }
 
     /**
+     * List of broadcasts in frozen processes that are yet to be enqueued.
+     */
+    private final ArrayList<BroadcastRecord> mFrozenBroadcasts = new ArrayList<>();
+
+    /**
      * List of broadcasts which are being delivered or yet to be delivered.
      */
     private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();
@@ -77,7 +82,12 @@
     final long[] mSummaryHistoryDispatchTime;
     final long[] mSummaryHistoryFinishTime;
 
+    void onBroadcastFrozenLocked(@NonNull BroadcastRecord r) {
+        mFrozenBroadcasts.add(r);
+    }
+
     void onBroadcastEnqueuedLocked(@NonNull BroadcastRecord r) {
+        mFrozenBroadcasts.remove(r);
         mPendingBroadcasts.add(r);
     }
 
@@ -101,7 +111,7 @@
         mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY);
     }
 
-    private final int ringAdvance(int x, final int increment, final int ringSize) {
+    private int ringAdvance(int x, final int increment, final int ringSize) {
         x += increment;
         if (x < 0) return (ringSize - 1);
         else if (x >= ringSize) return 0;
@@ -114,6 +124,10 @@
             final BroadcastRecord r = mPendingBroadcasts.get(i);
             r.dumpDebug(proto, BroadcastQueueProto.PENDING_BROADCASTS);
         }
+        for (int i = 0; i < mFrozenBroadcasts.size(); ++i) {
+            final BroadcastRecord r = mFrozenBroadcasts.get(i);
+            r.dumpDebug(proto, BroadcastQueueProto.FROZEN_BROADCASTS);
+        }
 
         int lastIndex = mHistoryNext;
         int ringIndex = lastIndex;
@@ -151,16 +165,8 @@
     public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage,
             @NonNull String queueName, @NonNull SimpleDateFormat sdf,
             boolean dumpAll, boolean needSep) {
-        pw.println("  Pending broadcasts:");
-        if (mPendingBroadcasts.isEmpty()) {
-            pw.println("    <empty>");
-        } else {
-            for (int idx = mPendingBroadcasts.size() - 1; idx >= 0; --idx) {
-                final BroadcastRecord r = mPendingBroadcasts.get(idx);
-                pw.print("  Broadcast #"); pw.print(idx); pw.println(":");
-                r.dump(pw, "    ", sdf);
-            }
-        }
+        dumpBroadcastList(pw, sdf, mFrozenBroadcasts, "Frozen");
+        dumpBroadcastList(pw, sdf, mPendingBroadcasts, "Pending");
 
         int i;
         boolean printed = false;
@@ -268,4 +274,18 @@
         }
         return needSep;
     }
+
+    private void dumpBroadcastList(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
+            @NonNull ArrayList<BroadcastRecord> broadcasts, @NonNull String flavor) {
+        pw.print("  "); pw.print(flavor); pw.println(" broadcasts:");
+        if (broadcasts.isEmpty()) {
+            pw.println("    <empty>");
+        } else {
+            for (int idx = broadcasts.size() - 1; idx >= 0; --idx) {
+                final BroadcastRecord r = broadcasts.get(idx);
+                pw.print(flavor); pw.print("  broadcast #"); pw.print(idx); pw.println(":");
+                r.dump(pw, "    ", sdf);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 4a37913..298eb79 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -44,6 +44,7 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayDeque;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Objects;
 
@@ -233,6 +234,11 @@
      */
     private long mForcedDelayedDurationMs;
 
+    /**
+     * List of outgoing broadcasts from a freezable process.
+     */
+    private final ArrayList<BroadcastRecord> mOutgoingBroadcasts = new ArrayList<>();
+
     public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
             @NonNull String processName, int uid) {
         this.constants = Objects.requireNonNull(constants);
@@ -250,6 +256,21 @@
         }
     }
 
+    public void enqueueOutgoingBroadcast(@NonNull BroadcastRecord record) {
+        mOutgoingBroadcasts.add(record);
+    }
+
+    public int getOutgoingBroadcastCount() {
+        return mOutgoingBroadcasts.size();
+    }
+
+    public void enqueueOutgoingBroadcasts(@NonNull BroadcastRecordConsumer consumer) {
+        for (int i = 0; i < mOutgoingBroadcasts.size(); ++i) {
+            consumer.accept(mOutgoingBroadcasts.get(i));
+        }
+        mOutgoingBroadcasts.clear();
+    }
+
     /**
      * Enqueue the given broadcast to be dispatched to this process at some
      * future point in time. The target receiver is indicated by the given index
@@ -386,8 +407,8 @@
     }
 
     /**
-     * Functional interface that tests a {@link BroadcastRecord} that has been
-     * previously enqueued in {@link BroadcastProcessQueue}.
+     * Functional interface that tests a {@link BroadcastRecord} and an index in the
+     * {@link BroadcastRecord} that has been previously enqueued in {@link BroadcastProcessQueue}.
      */
     @FunctionalInterface
     public interface BroadcastPredicate {
@@ -395,8 +416,8 @@
     }
 
     /**
-     * Functional interface that consumes a {@link BroadcastRecord} that has
-     * been previously enqueued in {@link BroadcastProcessQueue}.
+     * Functional interface that consumes a {@link BroadcastRecord} and an index in the
+     * {@link BroadcastRecord} that has been previously enqueued in {@link BroadcastProcessQueue}.
      */
     @FunctionalInterface
     public interface BroadcastConsumer {
@@ -404,6 +425,15 @@
     }
 
     /**
+     * Functional interface that consumes a {@link BroadcastRecord} that has
+     * been previously enqueued in {@link BroadcastProcessQueue}.
+     */
+    @FunctionalInterface
+    public interface BroadcastRecordConsumer {
+        void accept(@NonNull BroadcastRecord r);
+    }
+
+    /**
      * Invoke given consumer for any broadcasts matching given predicate. If
      * requested, matching broadcasts will also be removed from this queue.
      * <p>
@@ -774,6 +804,10 @@
         return mActiveIndex;
     }
 
+    public boolean isOutgoingEmpty() {
+        return mOutgoingBroadcasts.isEmpty();
+    }
+
     public boolean isEmpty() {
         return mPending.isEmpty() && mPendingUrgent.isEmpty() && mPendingOffload.isEmpty();
     }
@@ -1443,7 +1477,7 @@
 
     @NeverCompile
     public void dumpLocked(@UptimeMillisLong long now, @NonNull IndentingPrintWriter pw) {
-        if ((mActive == null) && isEmpty()) return;
+        if ((mActive == null) && isEmpty() && isOutgoingEmpty()) return;
 
         pw.print(toShortString());
         pw.print(" ");
@@ -1454,6 +1488,12 @@
         dumpProcessState(pw);
         dumpBroadcastCounts(pw);
 
+        if (!mOutgoingBroadcasts.isEmpty()) {
+            for (int i = 0; i < mOutgoingBroadcasts.size(); ++i) {
+                dumpOutgoingRecord(now, pw, mOutgoingBroadcasts.get(i));
+            }
+        }
+
         if (mActive != null) {
             dumpRecord("ACTIVE", now, pw, mActive, mActiveIndex);
         }
@@ -1525,6 +1565,15 @@
     }
 
     @NeverCompile
+    private void dumpOutgoingRecord(@UptimeMillisLong long now,
+            @NonNull IndentingPrintWriter pw, @NonNull BroadcastRecord record) {
+        pw.print("OUTGOING ");
+        TimeUtils.formatDuration(record.enqueueTime, now, pw);
+        pw.print(' ');
+        pw.println(record.toShortString());
+    }
+
+    @NeverCompile
     private void dumpRecord(@Nullable String flavor, @UptimeMillisLong long now,
             @NonNull IndentingPrintWriter pw, @NonNull BroadcastRecord record, int recordIndex) {
         TimeUtils.formatDuration(record.enqueueTime, now, pw);
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 4422608..569f9ec 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -92,6 +92,7 @@
 import com.android.server.LocalServices;
 import com.android.server.am.BroadcastProcessQueue.BroadcastConsumer;
 import com.android.server.am.BroadcastProcessQueue.BroadcastPredicate;
+import com.android.server.am.BroadcastProcessQueue.BroadcastRecordConsumer;
 import com.android.server.am.BroadcastRecord.DeliveryState;
 import com.android.server.pm.UserJourneyLogger;
 import com.android.server.pm.UserManagerInternal;
@@ -284,6 +285,9 @@
     // when the flag is fused on.
     private static final int MSG_DELIVERY_TIMEOUT_SOFT = 8;
 
+    // TODO: Use the trunk stable flag.
+    private static final boolean DEFER_FROZEN_OUTGOING_BCASTS = false;
+
     private void enqueueUpdateRunningList() {
         mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
         mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
@@ -332,9 +336,7 @@
                 return true;
             }
             case MSG_PROCESS_FREEZABLE_CHANGED: {
-                synchronized (mService) {
-                    refreshProcessQueueLocked((ProcessRecord) msg.obj);
-                }
+                handleProcessFreezableChanged((ProcessRecord) msg.obj);
                 return true;
             }
             case MSG_UID_STATE_CHANGED: {
@@ -435,7 +437,8 @@
         }
 
         // If app isn't running, and there's nothing in the queue, clean up
-        if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
+        if (queue.isEmpty() && queue.isOutgoingEmpty() && !queue.isActive()
+                && !queue.isProcessWarm()) {
             removeProcessQueue(queue.processName, queue.uid);
         }
     }
@@ -759,6 +762,21 @@
 
     @Override
     public void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {
+        // TODO: Apply delivery group policies and FLAG_REPLACE_PENDING to collapse the
+        // outgoing broadcasts.
+        // TODO: Add traces/logs for the enqueueing outgoing broadcasts logic.
+        if (DEFER_FROZEN_OUTGOING_BCASTS && isProcessFreezable(r.callerApp)) {
+            final BroadcastProcessQueue queue = getOrCreateProcessQueue(
+                    r.callerApp.processName, r.callerApp.uid);
+            if (queue.getOutgoingBroadcastCount() >= mConstants.MAX_FROZEN_OUTGOING_BROADCASTS) {
+                // TODO: Kill the process if the outgoing broadcasts count is
+                // beyond a certain limit.
+            }
+            queue.enqueueOutgoingBroadcast(r);
+            mHistory.onBroadcastFrozenLocked(r);
+            mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp);
+            return;
+        }
         if (DEBUG_BROADCAST) logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers");
 
         final int cookie = traceBegin("enqueueBroadcast");
@@ -1634,6 +1652,8 @@
                 "mBroadcastConsumerDeferClear");
     };
 
+    final BroadcastRecordConsumer mBroadcastRecordConsumerEnqueue = this::enqueueBroadcastLocked;
+
     /**
      * Verify that all known {@link #mProcessQueues} are in the state tested by
      * the given {@link Predicate}.
@@ -1930,8 +1950,9 @@
         }
     }
 
+    @VisibleForTesting
     @GuardedBy("mService")
-    private boolean isProcessFreezable(@Nullable ProcessRecord app) {
+    boolean isProcessFreezable(@Nullable ProcessRecord app) {
         if (app == null) {
             return false;
         }
@@ -1956,16 +1977,25 @@
         enqueueUpdateRunningList();
     }
 
+    private void handleProcessFreezableChanged(@NonNull ProcessRecord app) {
+        synchronized (mService) {
+            final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid);
+            if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) {
+                return;
+            }
+            if (!isProcessFreezable(app)) {
+                queue.enqueueOutgoingBroadcasts(mBroadcastRecordConsumerEnqueue);
+            }
+            refreshProcessQueueLocked(queue);
+        }
+    }
+
     /**
      * Refresh the process queue corresponding to {@code app} with the latest process state
      * so that runnableAt can be updated.
      */
     @GuardedBy("mService")
-    private void refreshProcessQueueLocked(@NonNull ProcessRecord app) {
-        final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid);
-        if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) {
-            return;
-        }
+    private void refreshProcessQueueLocked(@NonNull BroadcastProcessQueue queue) {
         setQueueProcess(queue, queue.app);
         enqueueUpdateRunningList();
     }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 150f406..0cf5575 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -1415,7 +1415,7 @@
 
     @GuardedBy({"mAm", "mProcLock"})
     private void freezeAppAsyncLSP(ProcessRecord app, @UptimeMillisLong long delayMillis) {
-        freezeAppAsyncInternalLSP(app, delayMillis, false);
+        freezeAppAsyncInternalLSP(app, delayMillis, false, false);
     }
 
     @GuardedBy({"mAm", "mProcLock"})
@@ -1423,11 +1423,25 @@
         freezeAppAsyncLSP(app, updateEarliestFreezableTime(app, 0));
     }
 
+    // TODO: Update freezeAppAsyncAtEarliestLSP to actually freeze the app at the earliest
+    // and remove this method.
+    @GuardedBy({"mAm", "mProcLock"})
+    void freezeAppAsyncImmediateLSP(ProcessRecord app) {
+        freezeAppAsyncInternalLSP(app, 0, false, true);
+    }
+
+    // TODO: Update this method to be private and have the existing clients call different methods.
+    // This "internal" method should not be directly triggered by clients outside this class.
     @GuardedBy({"mAm", "mProcLock"})
     void freezeAppAsyncInternalLSP(ProcessRecord app, @UptimeMillisLong long delayMillis,
-            boolean force) {
+            boolean force, boolean immediate) {
         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
         if (opt.isPendingFreeze()) {
+            if (immediate) {
+                mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
+                mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(
+                        SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app));
+            }
             // Skip redundant DO_FREEZE message
             return;
         }
@@ -2210,6 +2224,9 @@
                 case SET_FROZEN_PROCESS_MSG: {
                     ProcessRecord proc = (ProcessRecord) msg.obj;
                     synchronized (mAm) {
+                        if (!proc.mOptRecord.isPendingFreeze()) {
+                            return;
+                        }
                         freezeProcess(proc);
                     }
                     if (proc.mOptRecord.isFrozen()) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
index 9d3caa5..bd20ae2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BaseBroadcastQueueTest.java
@@ -283,4 +283,9 @@
         receiverList.add(res);
         return res;
     }
+
+    void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) {
+        app.mOptRecord.setPendingFreeze(pendingFreeze);
+        app.mOptRecord.setFrozen(frozen);
+    }
 }
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 bcf297f..079bc37 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -99,7 +99,7 @@
     private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
     private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
 
-    @Mock ProcessRecord mProcess;
+    ProcessRecord mProcess;
 
     @Mock BroadcastProcessQueue mQueue1;
     @Mock BroadcastProcessQueue mQueue2;
@@ -126,6 +126,9 @@
         doReturn(2L).when(mQueue2).getRunnableAt();
         doReturn(3L).when(mQueue3).getRunnableAt();
         doReturn(4L).when(mQueue4).getRunnableAt();
+
+        final ApplicationInfo ai = makeApplicationInfo(PACKAGE_ORANGE);
+        mProcess = spy(new ProcessRecord(mAms, ai, ai.processName, ai.uid));
     }
 
     @After
@@ -1746,6 +1749,31 @@
         }, false /* andRemove */);
     }
 
+    @Test
+    public void testIsProcessFreezable() throws Exception {
+        final ProcessRecord greenProcess = makeProcessRecord(makeApplicationInfo(PACKAGE_GREEN));
+
+        setProcessFreezable(greenProcess, true /* pendingFreeze */, true /* frozen */);
+        mImpl.onProcessFreezableChangedLocked(greenProcess);
+        waitForIdle();
+        assertTrue(mImpl.isProcessFreezable(greenProcess));
+
+        setProcessFreezable(greenProcess, true /* pendingFreeze */, false /* frozen */);
+        mImpl.onProcessFreezableChangedLocked(greenProcess);
+        waitForIdle();
+        assertTrue(mImpl.isProcessFreezable(greenProcess));
+
+        setProcessFreezable(greenProcess, false /* pendingFreeze */, true /* frozen */);
+        mImpl.onProcessFreezableChangedLocked(greenProcess);
+        waitForIdle();
+        assertTrue(mImpl.isProcessFreezable(greenProcess));
+
+        setProcessFreezable(greenProcess, false /* pendingFreeze */, false /* frozen */);
+        mImpl.onProcessFreezableChangedLocked(greenProcess);
+        waitForIdle();
+        assertFalse(mImpl.isProcessFreezable(greenProcess));
+    }
+
     // TODO: Reuse BroadcastQueueTest.makeActiveProcessRecord()
     private ProcessRecord makeProcessRecord(ApplicationInfo info) {
         final ProcessRecord r = spy(new ProcessRecord(mAms, info, info.processName, info.uid));
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 56e5bd6..66ab807 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -86,6 +86,7 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
@@ -446,11 +447,6 @@
                 BackgroundStartPrivileges.NONE, false, null, PROCESS_STATE_UNKNOWN);
     }
 
-    private void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) {
-        app.mOptRecord.setPendingFreeze(pendingFreeze);
-        app.mOptRecord.setFrozen(frozen);
-    }
-
     private void assertHealth() {
         // If this fails, it'll throw a clear reason message
         ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
@@ -2339,6 +2335,38 @@
                 .isGreaterThan(getReceiverScheduledTime(prioritizedRecord, receiverBlue));
     }
 
+    @Ignore
+    @Test
+    public void testDeferOutgoingBroadcasts() throws Exception {
+        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+        setProcessFreezable(callerApp, true /* pendingFreeze */, false /* frozen */);
+        mQueue.onProcessFreezableChangedLocked(callerApp);
+        waitForIdle();
+
+        final ProcessRecord receiverGreenApp = makeActiveProcessRecord(PACKAGE_GREEN);
+        final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
+        final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
+        enqueueBroadcast(makeBroadcastRecord(timeTick, callerApp, List.of(
+                makeRegisteredReceiver(receiverGreenApp),
+                makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+                makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+        waitForIdle();
+        verifyScheduleRegisteredReceiver(never(), receiverGreenApp, timeTick);
+        verifyScheduleReceiver(never(), receiverBlueApp, timeTick);
+        assertNull(mAms.getProcessRecordLocked(PACKAGE_YELLOW, getUidForPackage(PACKAGE_GREEN)));
+
+        setProcessFreezable(callerApp, false /* pendingFreeze */, false /* frozen */);
+        mQueue.onProcessFreezableChangedLocked(callerApp);
+        waitForIdle();
+
+        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
+        verifyScheduleReceiver(times(1), receiverBlueApp, timeTick);
+        final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+                getUidForPackage(PACKAGE_YELLOW));
+        verifyScheduleReceiver(times(1), receiverYellowApp, timeTick);
+    }
+
     private long getReceiverScheduledTime(@NonNull BroadcastRecord r, @NonNull Object receiver) {
         for (int i = 0; i < r.receivers.size(); ++i) {
             if (isReceiverEquals(receiver, r.receivers.get(i))) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
index 709a804..97b7af8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ServiceBindingOomAdjPolicyTest.java
@@ -186,7 +186,7 @@
                 any());
         doReturn(true).when(mAms.mOomAdjuster.mCachedAppOptimizer).useFreezer();
         doNothing().when(mAms.mOomAdjuster.mCachedAppOptimizer).freezeAppAsyncInternalLSP(
-                any(), anyLong(), anyBoolean());
+                any(), anyLong(), anyBoolean(), anyBoolean());
         doReturn(false).when(mAms.mAppProfiler).updateLowMemStateLSP(anyInt(), anyInt(),
                 anyInt(), anyLong());
 
@@ -503,7 +503,7 @@
         if (clientApp.isFreezable()) {
             verify(mAms.mOomAdjuster.mCachedAppOptimizer,
                     times(Flags.serviceBindingOomAdjPolicy() ? 1 : 0))
-                    .freezeAppAsyncInternalLSP(eq(clientApp), eq(0L), anyBoolean());
+                    .freezeAppAsyncInternalLSP(eq(clientApp), eq(0L), anyBoolean(), anyBoolean());
             clearInvocations(mAms.mOomAdjuster.mCachedAppOptimizer);
         }