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

Commit bdca950f authored by Sudheer Shanka's avatar Sudheer Shanka
Browse files

Avoid new allocations in enqueueBroadcastRecord().

Bug: 269066898
Test: atest ./services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
Test: atest ./services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
Change-Id: I874e1546f0f4294927e49d16b9d471610c875d78
parent 7f43a640
Loading
Loading
Loading
Loading
+28 −21
Original line number Diff line number Diff line
@@ -229,14 +229,19 @@ class BroadcastProcessQueue {
     * When defined, this receiver is considered "blocked" until at least the
     * given count of other receivers have reached a terminal state; typically
     * used for ordered broadcasts and priority traunches.
     *
     * @return the existing broadcast record in the queue that was replaced with a newer broadcast
     *         sent with {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} or {@code null} if there
     *         wasn't any broadcast that was replaced.
     */
    public void enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
            @NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
    @Nullable
    public BroadcastRecord enqueueOrReplaceBroadcast(@NonNull BroadcastRecord record,
            int recordIndex, boolean wouldBeSkipped) {
        if (record.isReplacePending()) {
            final boolean didReplace = replaceBroadcast(record, recordIndex,
                    replacedBroadcastConsumer, wouldBeSkipped);
            if (didReplace) {
                return;
            final BroadcastRecord replacedBroadcastRecord = replaceBroadcast(record, recordIndex,
                    wouldBeSkipped);
            if (replacedBroadcastRecord != null) {
                return replacedBroadcastRecord;
            }
        }

@@ -253,34 +258,37 @@ class BroadcastProcessQueue {
        // with implicit responsiveness expectations.
        getQueueForBroadcast(record).addLast(newBroadcastArgs);
        onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
        return null;
    }

    /**
     * Searches from newest to oldest in the pending broadcast queues, and at the first matching
     * pending broadcast it finds, replaces it in-place and returns -- does not attempt to handle
     * "duplicate" broadcasts in the queue.
     * <p>
     * @return {@code true} if it found and replaced an existing record in the queue;
     * {@code false} otherwise.
     *
     * @return the existing broadcast record in the queue that was replaced with a newer broadcast
     *         sent with {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} or {@code null} if there
     *         wasn't any broadcast that was replaced.
     */
    private boolean replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
            @NonNull BroadcastConsumer replacedBroadcastConsumer, boolean wouldBeSkipped) {
    @Nullable
    private BroadcastRecord replaceBroadcast(@NonNull BroadcastRecord record, int recordIndex,
            boolean wouldBeSkipped) {
        final ArrayDeque<SomeArgs> queue = getQueueForBroadcast(record);
        return replaceBroadcastInQueue(queue, record, recordIndex,
                replacedBroadcastConsumer, wouldBeSkipped);
        return replaceBroadcastInQueue(queue, record, recordIndex, wouldBeSkipped);
    }

    /**
     * Searches from newest to oldest, and at the first matching pending broadcast
     * it finds, replaces it in-place and returns -- does not attempt to handle
     * "duplicate" broadcasts in the queue.
     * <p>
     * @return {@code true} if it found and replaced an existing record in the queue;
     * {@code false} otherwise.
     *
     * @return the existing broadcast record in the queue that was replaced with a newer broadcast
     *         sent with {@link Intent#FLAG_RECEIVER_REPLACE_PENDING} or {@code null} if there
     *         wasn't any broadcast that was replaced.
     */
    private boolean replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
    @Nullable
    private BroadcastRecord replaceBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
            @NonNull BroadcastRecord record, int recordIndex,
            @NonNull BroadcastConsumer replacedBroadcastConsumer,
            boolean wouldBeSkipped) {
        final Iterator<SomeArgs> it = queue.descendingIterator();
        final Object receiver = record.receivers.get(recordIndex);
@@ -302,11 +310,10 @@ class BroadcastProcessQueue {
                record.copyEnqueueTimeFrom(testRecord);
                onBroadcastDequeued(testRecord, testRecordIndex, testWouldBeSkipped);
                onBroadcastEnqueued(record, recordIndex, wouldBeSkipped);
                replacedBroadcastConsumer.accept(testRecord, testRecordIndex);
                return true;
                return testRecord;
            }
        }
        return false;
        return null;
    }

    /**
+22 −4
Original line number Diff line number Diff line
@@ -92,6 +92,7 @@ import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
import java.util.function.Predicate;
@@ -204,6 +205,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    @GuardedBy("mService")
    private final ArrayList<Pair<BooleanSupplier, CountDownLatch>> mWaitingFor = new ArrayList<>();

    /**
     * Container for holding the set of broadcasts that have been replaced by a newer broadcast
     * sent with {@link Intent#FLAG_RECEIVER_REPLACE_PENDING}.
     */
    @GuardedBy("mService")
    private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache =
            new AtomicReference<>();

    private final BroadcastConstants mConstants;
    private final BroadcastConstants mFgConstants;
    private final BroadcastConstants mBgConstants;
@@ -627,9 +636,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        r.enqueueRealTime = SystemClock.elapsedRealtime();
        r.enqueueClockTime = System.currentTimeMillis();

        final ArraySet<BroadcastRecord> replacedBroadcasts = new ArraySet<>();
        final BroadcastConsumer replacedBroadcastConsumer =
                (record, i) -> replacedBroadcasts.add(record);
        ArraySet<BroadcastRecord> replacedBroadcasts = mReplacedBroadcastsCache.getAndSet(null);
        if (replacedBroadcasts == null) {
            replacedBroadcasts = new ArraySet<>();
        }
        boolean enqueuedBroadcast = false;

        for (int i = 0; i < r.receivers.size(); i++) {
@@ -653,7 +663,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
                }
            }
            enqueuedBroadcast = true;
            queue.enqueueOrReplaceBroadcast(r, i, replacedBroadcastConsumer, wouldBeSkipped);
            final BroadcastRecord replacedBroadcast = queue.enqueueOrReplaceBroadcast(
                    r, i, wouldBeSkipped);
            if (replacedBroadcast != null) {
                replacedBroadcasts.add(replacedBroadcast);
            }
            if (r.isDeferUntilActive() && queue.isDeferredUntilActive()) {
                setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
                        "deferred at enqueue time");
@@ -664,7 +678,11 @@ class BroadcastQueueModernImpl extends BroadcastQueue {

        // Skip any broadcasts that have been replaced by newer broadcasts with
        // FLAG_RECEIVER_REPLACE_PENDING.
        // TODO: Optimize and reuse mBroadcastConsumerSkipAndCanceled for the case of
        // cancelling all receivers for a broadcast.
        skipAndCancelReplacedBroadcasts(replacedBroadcasts);
        replacedBroadcasts.clear();
        mReplacedBroadcastsCache.compareAndSet(null, replacedBroadcasts);

        // If nothing to dispatch, send any pending result immediately
        if (r.receivers.isEmpty() || !enqueuedBroadcast) {
+14 −28
Original line number Diff line number Diff line
@@ -223,8 +223,7 @@ public class BroadcastQueueModernImplTest {

    private void enqueueOrReplaceBroadcast(BroadcastProcessQueue queue,
            BroadcastRecord record, int recordIndex, long enqueueTime) {
        queue.enqueueOrReplaceBroadcast(record, recordIndex,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(record, recordIndex, false);
        record.enqueueTime = enqueueTime;
    }

@@ -354,8 +353,7 @@ public class BroadcastQueueModernImplTest {
        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
                List.of(makeMockRegisteredReceiver()));
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);

        queue.setProcessCached(false);
        final long notCachedRunnableAt = queue.getRunnableAt();
@@ -377,14 +375,12 @@ public class BroadcastQueueModernImplTest {
        // enqueue a bg-priority broadcast then a fg-priority one
        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
        final BroadcastRecord timezoneRecord = makeBroadcastRecord(timezone);
        queue.enqueueOrReplaceBroadcast(timezoneRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(timezoneRecord, 0, false);

        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
        final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);

        // verify that:
        // (a) the queue is immediately runnable by existence of a fg-priority broadcast
@@ -415,8 +411,7 @@ public class BroadcastQueueModernImplTest {
        final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane, null,
                List.of(withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 10),
                        withPriority(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN), 0)), true);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 1,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 1, false);

        assertFalse(queue.isRunnable());
        assertEquals(BroadcastProcessQueue.REASON_BLOCKED, queue.getRunnableAtReason());
@@ -439,8 +434,7 @@ public class BroadcastQueueModernImplTest {
        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
        final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane,
                List.of(makeMockRegisteredReceiver()));
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);

        mConstants.MAX_PENDING_BROADCASTS = 128;
        queue.invalidateRunnableAt();
@@ -466,13 +460,11 @@ public class BroadcastQueueModernImplTest {
                new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
                List.of(makeMockRegisteredReceiver()));

        queue.enqueueOrReplaceBroadcast(lazyRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(lazyRecord, 0, false);
        assertThat(queue.getRunnableAt()).isGreaterThan(lazyRecord.enqueueTime);
        assertThat(queue.getRunnableAtReason()).isNotEqualTo(testRunnableAtReason);

        queue.enqueueOrReplaceBroadcast(testRecord, 0,
                null /* replacedBroadcastConsumer */, false);
        queue.enqueueOrReplaceBroadcast(testRecord, 0, false);
        assertThat(queue.getRunnableAt()).isAtMost(testRecord.enqueueTime);
        assertThat(queue.getRunnableAtReason()).isEqualTo(testRunnableAtReason);
    }
@@ -534,26 +526,20 @@ public class BroadcastQueueModernImplTest {

        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
                null /* replacedBroadcastConsumer */, false);
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0,
                null /* replacedBroadcastConsumer */, false);
                makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0, false);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED)
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
                null /* replacedBroadcastConsumer */, false);
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0,
                null /* replacedBroadcastConsumer */, false);
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0, false);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0,
                null /* replacedBroadcastConsumer */, false);
                makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0, false);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0,
                null /* replacedBroadcastConsumer */, false);
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0, false);

        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_LOCKED_BOOT_COMPLETED, queue.getActive().intent.getAction());