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

Commit 2d8ec53e authored by Jeff Sharkey's avatar Jeff Sharkey
Browse files

BroadcastQueue: add support for wait-for-barrier.

We recently added this feature to help tests finish faster without
needing to wait for complete idle, and this change expands support
to the "modern" queue, along with tests.

Fix bugs in "default" implementation where we need to consult all
pending broadcasts in the dispatcher.

Bug: 245771249
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Change-Id: I9be5412b1d6020212ceae674d9f3685c3bdec3c7
parent c401df9f
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -20,7 +20,9 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_DEFERRAL;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.os.Handler;
@@ -540,6 +542,38 @@ public class BroadcastDispatcher {
        }
    }

    private static boolean isDeferralsBeyondBarrier(@NonNull ArrayList<Deferrals> list,
            @UptimeMillisLong long barrierTime) {
        for (int i = 0; i < list.size(); i++) {
            if (!isBeyondBarrier(list.get(i).broadcasts, barrierTime)) {
                return false;
            }
        }
        return true;
    }

    private static boolean isBeyondBarrier(@NonNull ArrayList<BroadcastRecord> list,
            @UptimeMillisLong long barrierTime) {
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).enqueueTime <= barrierTime) {
                return false;
            }
        }
        return true;
    }

    public boolean isBeyondBarrier(@UptimeMillisLong long barrierTime) {
        synchronized (mLock) {
            if ((mCurrentBroadcast != null) && mCurrentBroadcast.enqueueTime <= barrierTime) {
                return false;
            }
            return isBeyondBarrier(mOrderedBroadcasts, barrierTime)
                    && isBeyondBarrier(mAlarmQueue, barrierTime)
                    && isDeferralsBeyondBarrier(mDeferredBroadcasts, barrierTime)
                    && isDeferralsBeyondBarrier(mAlarmDeferrals, barrierTime);
        }
    }

    private static int pendingInDeferralsList(ArrayList<Deferrals> list) {
        int pending = 0;
        final int numEntries = list.size();
+16 −0
Original line number Diff line number Diff line
@@ -407,6 +407,22 @@ class BroadcastProcessQueue {
        return !isActive() && isEmpty();
    }

    /**
     * Quickly determine if this queue has broadcasts enqueued before the given
     * barrier timestamp that are still waiting to be delivered.
     */
    public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
        if (mActive != null) {
            return mActive.enqueueTime > barrierTime;
        }
        final SomeArgs next = mPending.peekFirst();
        if (next != null) {
            return ((BroadcastRecord) next.arg1).enqueueTime > barrierTime;
        }
        // Nothing running or runnable means we're past the barrier
        return true;
    }

    public boolean isRunnable() {
        if (mRunnableAtInvalidated) updateRunnableAt();
        return mRunnableAt != Long.MAX_VALUE;
+21 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.am;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
@@ -60,14 +61,22 @@ public abstract class BroadcastQueue {
        }
    }

    static void logw(String msg) {
    static void logw(@NonNull String msg) {
        Slog.w(TAG, msg);
    }

    static void logv(String msg) {
    static void logv(@NonNull String msg) {
        Slog.v(TAG, msg);
    }

    static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
        logv(msg);
        if (pw != null) {
            pw.println(msg);
            pw.flush();
        }
    }

    @Override
    public String toString() {
        return mQueueName;
@@ -166,6 +175,16 @@ public abstract class BroadcastQueue {
    @GuardedBy("mService")
    public abstract boolean isIdleLocked();

    /**
     * Quickly determine if this queue has broadcasts enqueued before the given
     * barrier timestamp that are still waiting to be delivered.
     *
     * @see #waitForIdle
     * @see #waitForBarrier
     */
    @GuardedBy("mService")
    public abstract boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime);

    /**
     * Wait until this queue becomes completely idle.
     * <p>
+8 −7
Original line number Diff line number Diff line
@@ -1753,19 +1753,20 @@ public class BroadcastQueueImpl extends BroadcastQueue {
        // If nothing active, we're beyond barrier
        if (isIdleLocked()) return true;

        // Check if active broadcast is beyond barrier
        final BroadcastRecord active = getActiveBroadcastLocked();
        if (active != null && active.enqueueTime > barrierTime) {
            return true;
        // Check if parallel broadcasts are beyond barrier
        for (int i = 0; i < mParallelBroadcasts.size(); i++) {
            if (mParallelBroadcasts.get(i).enqueueTime <= barrierTime) {
                return false;
            }
        }

        // Check if pending broadcast is beyond barrier
        final BroadcastRecord pending = getPendingBroadcastLocked();
        if (pending != null && pending.enqueueTime > barrierTime) {
            return true;
        if ((pending != null) && pending.enqueueTime <= barrierTime) {
            return false;
        }

        return false;
        return mDispatcher.isBeyondBarrier(barrierTime);
    }

    public void waitForIdle(PrintWriter pw) {
+73 −16
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.IApplicationThread;
@@ -63,6 +64,7 @@ import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -83,6 +85,7 @@ import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.function.BooleanSupplier;
import java.util.function.Predicate;

/**
@@ -188,10 +191,20 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    private @Nullable BroadcastProcessQueue mRunningColdStart;

    /**
     * Collection of latches waiting for queue to go idle.
     * Collection of latches waiting for device to reach specific state. The
     * first argument is a function to test for the desired state, and the
     * second argument is the latch to release once that state is reached.
     * <p>
     * This is commonly used for callers that are blocked waiting for an
     * {@link #isIdleLocked} or {@link #isBeyondBarrierLocked} to be reached,
     * without requiring that they periodically poll for the state change.
     * <p>
     * Finally, the presence of any waiting latches will cause all
     * future-runnable processes to be runnable immediately, to aid in reaching
     * the desired state as quickly as possible.
     */
    @GuardedBy("mService")
    private final ArrayList<CountDownLatch> mWaitingForIdle = new ArrayList<>();
    private final ArrayList<Pair<BooleanSupplier, CountDownLatch>> mWaitingFor = new ArrayList<>();

    private final BroadcastConstants mConstants;
    private final BroadcastConstants mFgConstants;
@@ -321,8 +334,8 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        final int cookie = traceBegin(TAG, "updateRunningList");
        final long now = SystemClock.uptimeMillis();

        // If someone is waiting to go idle, everything is runnable now
        final boolean waitingForIdle = !mWaitingForIdle.isEmpty();
        // If someone is waiting for a state, everything is runnable now
        final boolean waitingFor = !mWaitingFor.isEmpty();

        // We're doing an update now, so remove any future update requests;
        // we'll repost below if needed
@@ -336,7 +349,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

            // If queues beyond this point aren't ready to run yet, schedule
            // another pass when they'll be runnable
            if (runnableAt > now && !waitingForIdle) {
            if (runnableAt > now && !waitingFor) {
                mLocalHandler.sendEmptyMessageAtTime(MSG_UPDATE_RUNNING_LIST, runnableAt);
                break;
            }
@@ -402,9 +415,15 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
            mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
        }

        if (waitingForIdle && isIdleLocked()) {
            mWaitingForIdle.forEach((latch) -> latch.countDown());
            mWaitingForIdle.clear();
        if (waitingFor) {
            mWaitingFor.removeIf((pair) -> {
                if (pair.first.getAsBoolean()) {
                    pair.second.countDown();
                    return true;
                } else {
                    return false;
                }
            });
        }

        if (CHECK_CONSISTENCY) checkConsistencyLocked();
@@ -953,6 +972,26 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        r.resultExtras = null;
    };

    /**
     * Verify that all known {@link #mProcessQueues} are in the state tested by
     * the given {@link Predicate}.
     */
    private boolean testAllProcessQueues(@NonNull Predicate<BroadcastProcessQueue> test,
            @NonNull String label, @Nullable PrintWriter pw) {
        for (int i = 0; i < mProcessQueues.size(); i++) {
            BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
            while (leaf != null) {
                if (!test.test(leaf)) {
                    logv("Test " + label + " failed due to " + leaf.toShortString(), pw);
                    return false;
                }
                leaf = leaf.processNameNext;
            }
        }
        logv("Test " + label + " passed", pw);
        return true;
    }

    private boolean forEachMatchingBroadcast(
            @NonNull Predicate<BroadcastProcessQueue> queuePredicate,
            @NonNull BroadcastPredicate broadcastPredicate,
@@ -1000,14 +1039,38 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

    @Override
    public boolean isIdleLocked() {
        return (mRunnableHead == null) && (getRunningSize() == 0);
        return isIdleLocked(null);
    }

    public boolean isIdleLocked(@Nullable PrintWriter pw) {
        return testAllProcessQueues(q -> q.isIdle(), "idle", pw);
    }

    @Override
    public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
        return isBeyondBarrierLocked(barrierTime, null);
    }

    public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime,
            @Nullable PrintWriter pw) {
        return testAllProcessQueues(q -> q.isBeyondBarrierLocked(barrierTime), "barrier", pw);
    }

    @Override
    public void waitForIdle(@Nullable PrintWriter pw) {
        waitFor(() -> isIdleLocked(pw));
    }

    @Override
    public void waitForBarrier(@Nullable PrintWriter pw) {
        final long now = SystemClock.uptimeMillis();
        waitFor(() -> isBeyondBarrierLocked(now, pw));
    }

    public void waitFor(@NonNull BooleanSupplier condition) {
        final CountDownLatch latch = new CountDownLatch(1);
        synchronized (mService) {
            mWaitingForIdle.add(latch);
            mWaitingFor.add(Pair.create(condition, latch));
        }
        enqueueUpdateRunningList();
        try {
@@ -1017,12 +1080,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        }
    }

    @Override
    public void waitForBarrier(@Nullable PrintWriter pw) {
        // TODO: implement
        throw new UnsupportedOperationException();
    }

    @Override
    public String describeStateLocked() {
        return getRunningSize() + " running";
Loading