Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4b5ce492 authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

Use "frozen" state for broadcasts.

We originally had been using registerUidObserver() to observe changes
to the "cached" state changes of apps, since that's all we had at
the time.  Since then, registerUidFrozenStateChangedCallback() has
been added, so this change pivots to using that.

Even though we internally have the ability to control per-process
broadcast queues, both of these listeners only provide UID-level
granularity, so we follow their lead.  If the freezer team adds PID-
level events in the future, we can easily adapt.

Bug: 276243219, 275142200, 275594411
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Test: atest FrameworksMockingServicesTests:BroadcastRecordTest
Change-Id: Ia33cca1034fba9225f1fdbe97c216f077c4d9dba
parent 646c4e0f
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -18769,6 +18769,7 @@ public class ActivityManagerService extends IActivityManager.Stub
        // too quickly in parallel below
        pingCount.incrementAndGet();
        synchronized (this) {
        synchronized (mProcLock) {
            final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
                    mProcessList.getProcessNamesLOSP().getMap();
@@ -18793,6 +18794,7 @@ public class ActivityManagerService extends IActivityManager.Stub
                }
            }
        }
        }
        // Now that we've dispatched all "ping" events above, we can send our
        // "pong" sentinel value
+8 −8
Original line number Diff line number Diff line
@@ -203,11 +203,11 @@ public class BroadcastConstants {

    /**
     * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
     * targeting cached applications.
     * targeting frozen applications.
     */
    public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
    private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
    private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
    public long DELAY_FROZEN_MILLIS = DEFAULT_DELAY_FROZEN_MILLIS;
    private static final String KEY_DELAY_FROZEN_MILLIS = "bcast_delay_frozen_millis";
    private static final long DEFAULT_DELAY_FROZEN_MILLIS = +120_000;

    /**
     * For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
@@ -373,8 +373,8 @@ public class BroadcastConstants {
                    DEFAULT_MAX_PENDING_BROADCASTS);
            DELAY_NORMAL_MILLIS = getDeviceConfigLong(KEY_DELAY_NORMAL_MILLIS,
                    DEFAULT_DELAY_NORMAL_MILLIS);
            DELAY_CACHED_MILLIS = getDeviceConfigLong(KEY_DELAY_CACHED_MILLIS,
                    DEFAULT_DELAY_CACHED_MILLIS);
            DELAY_FROZEN_MILLIS = getDeviceConfigLong(KEY_DELAY_FROZEN_MILLIS,
                    DEFAULT_DELAY_FROZEN_MILLIS);
            DELAY_URGENT_MILLIS = getDeviceConfigLong(KEY_DELAY_URGENT_MILLIS,
                    DEFAULT_DELAY_URGENT_MILLIS);
            MAX_HISTORY_COMPLETE_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_COMPLETE_SIZE,
@@ -421,8 +421,8 @@ public class BroadcastConstants {
            pw.print(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS).println();
            pw.print(KEY_DELAY_NORMAL_MILLIS,
                    TimeUtils.formatDuration(DELAY_NORMAL_MILLIS)).println();
            pw.print(KEY_DELAY_CACHED_MILLIS,
                    TimeUtils.formatDuration(DELAY_CACHED_MILLIS)).println();
            pw.print(KEY_DELAY_FROZEN_MILLIS,
                    TimeUtils.formatDuration(DELAY_FROZEN_MILLIS)).println();
            pw.print(KEY_DELAY_URGENT_MILLIS,
                    TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
            pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
+20 −23
Original line number Diff line number Diff line
@@ -188,7 +188,7 @@ class BroadcastProcessQueue {
    private @Reason int mRunnableAtReason = REASON_EMPTY;
    private boolean mRunnableAtInvalidated;

    private boolean mProcessCached;
    private boolean mUidFrozen;
    private boolean mProcessInstrumented;
    private boolean mProcessPersistent;

@@ -385,11 +385,9 @@ class BroadcastProcessQueue {
    public void setProcess(@Nullable ProcessRecord app) {
        this.app = app;
        if (app != null) {
            setProcessCached(app.isCached());
            setProcessInstrumented(app.getActiveInstrumentation() != null);
            setProcessPersistent(app.isPersistent());
        } else {
            setProcessCached(false);
            setProcessInstrumented(false);
            setProcessPersistent(false);
        }
@@ -400,13 +398,12 @@ class BroadcastProcessQueue {
    }

    /**
     * Update if this process is in the "cached" state, typically signaling that
     * Update if this UID is in the "frozen" state, typically signaling that
     * broadcast dispatch should be paused or delayed.
     */
    @VisibleForTesting
    void setProcessCached(boolean cached) {
        if (mProcessCached != cached) {
            mProcessCached = cached;
    public void setUidFrozen(boolean frozen) {
        if (mUidFrozen != frozen) {
            mUidFrozen = frozen;
            invalidateRunnableAt();
        }
    }
@@ -813,7 +810,7 @@ class BroadcastProcessQueue {

    public boolean isDeferredUntilActive() {
        if (mRunnableAtInvalidated) updateRunnableAt();
        return mRunnableAtReason == BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER;
        return mRunnableAtReason == BroadcastProcessQueue.REASON_INFINITE_DEFER;
    }

    public boolean hasDeferredBroadcasts() {
@@ -848,14 +845,14 @@ class BroadcastProcessQueue {
    }

    static final int REASON_EMPTY = 0;
    static final int REASON_CACHED = 1;
    static final int REASON_FROZEN = 1;
    static final int REASON_NORMAL = 2;
    static final int REASON_MAX_PENDING = 3;
    static final int REASON_BLOCKED = 4;
    static final int REASON_INSTRUMENTED = 5;
    static final int REASON_PERSISTENT = 6;
    static final int REASON_FORCE_DELAYED = 7;
    static final int REASON_CACHED_INFINITE_DEFER = 8;
    static final int REASON_INFINITE_DEFER = 8;
    static final int REASON_CONTAINS_FOREGROUND = 10;
    static final int REASON_CONTAINS_ORDERED = 11;
    static final int REASON_CONTAINS_ALARM = 12;
@@ -867,14 +864,14 @@ class BroadcastProcessQueue {

    @IntDef(flag = false, prefix = { "REASON_" }, value = {
            REASON_EMPTY,
            REASON_CACHED,
            REASON_FROZEN,
            REASON_NORMAL,
            REASON_MAX_PENDING,
            REASON_BLOCKED,
            REASON_INSTRUMENTED,
            REASON_PERSISTENT,
            REASON_FORCE_DELAYED,
            REASON_CACHED_INFINITE_DEFER,
            REASON_INFINITE_DEFER,
            REASON_CONTAINS_FOREGROUND,
            REASON_CONTAINS_ORDERED,
            REASON_CONTAINS_ALARM,
@@ -890,14 +887,14 @@ class BroadcastProcessQueue {
    static @NonNull String reasonToString(@Reason int reason) {
        switch (reason) {
            case REASON_EMPTY: return "EMPTY";
            case REASON_CACHED: return "CACHED";
            case REASON_FROZEN: return "FROZEN";
            case REASON_NORMAL: return "NORMAL";
            case REASON_MAX_PENDING: return "MAX_PENDING";
            case REASON_BLOCKED: return "BLOCKED";
            case REASON_INSTRUMENTED: return "INSTRUMENTED";
            case REASON_PERSISTENT: return "PERSISTENT";
            case REASON_FORCE_DELAYED: return "FORCE_DELAYED";
            case REASON_CACHED_INFINITE_DEFER: return "INFINITE_DEFER";
            case REASON_INFINITE_DEFER: return "INFINITE_DEFER";
            case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
            case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
            case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
@@ -978,12 +975,12 @@ class BroadcastProcessQueue {
            } else if (mProcessPersistent) {
                mRunnableAt = runnableAt;
                mRunnableAtReason = REASON_PERSISTENT;
            } else if (mProcessCached) {
            } else if (mUidFrozen) {
                if (r.deferUntilActive) {
                    // All enqueued broadcasts are deferrable, defer
                    if (mCountDeferred == mCountEnqueued) {
                        mRunnableAt = Long.MAX_VALUE;
                        mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;
                        mRunnableAtReason = REASON_INFINITE_DEFER;
                    } else {
                        // At least one enqueued broadcast isn't deferrable, repick time and reason
                        // for this record. If a later record is not deferrable and is one of these
@@ -998,14 +995,14 @@ class BroadcastProcessQueue {
                            mRunnableAt = runnableAt;
                            mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
                        } else {
                            mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
                            mRunnableAtReason = REASON_CACHED;
                            mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
                            mRunnableAtReason = REASON_FROZEN;
                        }
                    }
                } else {
                    // This record isn't deferrable
                    mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
                    mRunnableAtReason = REASON_CACHED;
                    mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
                    mRunnableAtReason = REASON_FROZEN;
                }
            } else if (mCountResultTo > 0) {
                // All resultTo broadcasts are infinitely deferrable, so if the app
@@ -1187,8 +1184,8 @@ class BroadcastProcessQueue {
    @NeverCompile
    private void dumpProcessState(@NonNull IndentingPrintWriter pw) {
        final StringBuilder sb = new StringBuilder();
        if (mProcessCached) {
            sb.append("CACHED");
        if (mUidFrozen) {
            sb.append("FROZEN");
        }
        if (mProcessInstrumented) {
            if (sb.length() > 0) sb.append("|");
+47 −14
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.am;

import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;

@@ -47,7 +48,7 @@ import android.app.ActivityManager;
import android.app.ApplicationExitInfo;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.UidObserver;
import android.app.IUidFrozenStateChangedCallback;
import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -71,6 +72,7 @@ import android.util.MathUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;

@@ -208,6 +210,16 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache =
            new AtomicReference<>();

    /**
     * Map from UID to its last known "frozen" state.
     * <p>
     * We manually maintain this data structure since the lifecycle of
     * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be
     * mismatched.
     */
    @GuardedBy("mService")
    private final SparseBooleanArray mUidFrozen = new SparseBooleanArray();

    private final BroadcastConstants mConstants;
    private final BroadcastConstants mFgConstants;
    private final BroadcastConstants mBgConstants;
@@ -485,6 +497,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        final BroadcastProcessQueue queue = getProcessQueue(app);
        if (queue != null) {
            queue.setProcess(app);
            queue.setUidFrozen(mUidFrozen.get(queue.uid, false));
        }

        boolean didSomething = false;
@@ -526,6 +539,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        final BroadcastProcessQueue queue = getProcessQueue(app);
        if (queue != null) {
            queue.setProcess(null);
            queue.setUidFrozen(mUidFrozen.get(queue.uid, false));
        }

        if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
@@ -1325,15 +1339,25 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        mFgConstants.startObserving(mHandler, resolver);
        mBgConstants.startObserving(mHandler, resolver);

        mService.registerUidObserver(new UidObserver() {
        mService.registerUidFrozenStateChangedCallback(new IUidFrozenStateChangedCallback.Stub() {
            @Override
            public void onUidCachedChanged(int uid, boolean cached) {
            public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
                synchronized (mService) {
                    for (int i = 0; i < uids.length; i++) {
                        final int uid = uids[i];
                        final boolean frozen = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
                        if (frozen) {
                            mUidFrozen.put(uid, true);
                        } else {
                            mUidFrozen.delete(uid);
                        }

                        BroadcastProcessQueue leaf = mProcessQueues.get(uid);
                        while (leaf != null) {
                            // Update internal state by refreshing values previously
                            // read from any known running process
                            leaf.setProcess(leaf.app);
                            leaf.setUidFrozen(frozen);
                            updateQueueDeferred(leaf);
                            updateRunnableList(leaf);
                            leaf = leaf.processNameNext;
@@ -1341,7 +1365,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
                        enqueueUpdateRunningList();
                    }
                }
        }, ActivityManager.UID_OBSERVER_CACHED, 0, "android");
            }
        });

        // Kick off periodic health checks
        mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1496,6 +1521,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) {
        if (!queue.isProcessWarm()) {
            queue.setProcess(mService.getProcessRecordLocked(queue.processName, queue.uid));
            queue.setUidFrozen(mUidFrozen.get(queue.uid, false));
        }
    }

@@ -1690,6 +1716,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

        BroadcastProcessQueue created = new BroadcastProcessQueue(mConstants, processName, uid);
        created.setProcess(mService.getProcessRecordLocked(processName, uid));
        created.setUidFrozen(mUidFrozen.get(uid, false));

        if (leaf == null) {
            mProcessQueues.put(uid, created);
@@ -1818,6 +1845,12 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        ipw.decreaseIndent();
        ipw.println();

        ipw.println("Frozen UIDs:");
        ipw.increaseIndent();
        ipw.println(mUidFrozen.toString());
        ipw.decreaseIndent();
        ipw.println();

        if (dumpConstants) {
            mConstants.dump(ipw);
        }
+23 −23
Original line number Diff line number Diff line
@@ -129,7 +129,7 @@ public final class BroadcastQueueModernImplTest {
        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
        mConstants.DELAY_URGENT_MILLIS = -120_000;
        mConstants.DELAY_NORMAL_MILLIS = 10_000;
        mConstants.DELAY_CACHED_MILLIS = 120_000;
        mConstants.DELAY_FROZEN_MILLIS = 120_000;

        final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
            public boolean shouldSkip(BroadcastRecord r, Object o) {
@@ -371,13 +371,13 @@ public final class BroadcastQueueModernImplTest {
                List.of(makeMockRegisteredReceiver()), false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);

        queue.setProcessCached(false);
        queue.setUidFrozen(false);
        final long notCachedRunnableAt = queue.getRunnableAt();
        queue.setProcessCached(true);
        queue.setUidFrozen(true);
        final long cachedRunnableAt = queue.getRunnableAt();
        assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
        assertFalse(queue.isRunnable());
        assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
        assertEquals(BroadcastProcessQueue.REASON_INFINITE_DEFER,
                queue.getRunnableAtReason());
        assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
    }
@@ -398,13 +398,13 @@ public final class BroadcastQueueModernImplTest {
                List.of(makeMockRegisteredReceiver()), false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);

        queue.setProcessCached(false);
        queue.setUidFrozen(false);
        final long notCachedRunnableAt = queue.getRunnableAt();
        queue.setProcessCached(true);
        queue.setUidFrozen(true);
        final long cachedRunnableAt = queue.getRunnableAt();
        assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
        assertTrue(queue.isRunnable());
        assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
        assertEquals(BroadcastProcessQueue.REASON_FROZEN, queue.getRunnableAtReason());
        assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
    }

@@ -430,13 +430,13 @@ public final class BroadcastQueueModernImplTest {
        // verify that:
        // (a) the queue is immediately runnable by existence of a fg-priority broadcast
        // (b) the next one up is the fg-priority broadcast despite its later enqueue time
        queue.setProcessCached(false);
        queue.setUidFrozen(false);
        assertTrue(queue.isRunnable());
        assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
        assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);

        queue.setProcessCached(true);
        queue.setUidFrozen(true);
        assertTrue(queue.isRunnable());
        assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
        assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
@@ -496,10 +496,10 @@ public final class BroadcastQueueModernImplTest {
     * Verify that a cached process that would normally be delayed becomes
     * immediately runnable when the given broadcast is enqueued.
     */
    private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) {
    private void doRunnableAt_Frozen(BroadcastRecord testRecord, int testRunnableAtReason) {
        final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
                PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
        queue.setProcessCached(true);
        queue.setUidFrozen(true);

        final BroadcastRecord lazyRecord = makeBroadcastRecord(
                new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
@@ -515,49 +515,49 @@ public final class BroadcastQueueModernImplTest {
    }

    @Test
    public void testRunnableAt_Cached_Manifest() {
        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
    public void testRunnableAt_Frozen_Manifest() {
        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
                List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST);
    }

    @Test
    public void testRunnableAt_Cached_Ordered() {
        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
    public void testRunnableAt_Frozen_Ordered() {
        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
                List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED);
    }

    @Test
    public void testRunnableAt_Cached_Foreground() {
    public void testRunnableAt_Frozen_Foreground() {
        final Intent foregroundIntent = new Intent();
        foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        doRunnableAt_Cached(makeBroadcastRecord(foregroundIntent, null,
        doRunnableAt_Frozen(makeBroadcastRecord(foregroundIntent, null,
                List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_FOREGROUND);
    }

    @Test
    public void testRunnableAt_Cached_Interactive() {
    public void testRunnableAt_Frozen_Interactive() {
        final BroadcastOptions options = BroadcastOptions.makeBasic();
        options.setInteractive(true);
        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_INTERACTIVE);
    }

    @Test
    public void testRunnableAt_Cached_Alarm() {
    public void testRunnableAt_Frozen_Alarm() {
        final BroadcastOptions options = BroadcastOptions.makeBasic();
        options.setAlarmBroadcast(true);
        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM);
    }

    @Test
    public void testRunnableAt_Cached_Prioritized_NonDeferrable() {
    public void testRunnableAt_Frozen_Prioritized_NonDeferrable() {
        final List receivers = List.of(
                withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
                withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
        final BroadcastOptions options = BroadcastOptions.makeBasic()
                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                receivers, null, false), REASON_CONTAINS_PRIORITIZED);
    }

Loading