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

Commit fff9b381 authored by Chris Tate's avatar Chris Tate Committed by Android (Google) Code Review
Browse files

Merge "Don't let urgent broadcasts starve non-urgent"

parents b09a41ab d5de3154
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -153,6 +153,11 @@ public class BroadcastConstants {
            "bcast_extra_running_urgent_process_queues";
    private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1;

    public int MAX_CONSECUTIVE_URGENT_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES;
    private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES =
            "bcast_max_consecutive_urgent_dispatches";
    private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3;

    /**
     * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
     * to dispatch to a "running" process queue before we retire them back to
@@ -333,6 +338,9 @@ public class BroadcastConstants {
            EXTRA_RUNNING_URGENT_PROCESS_QUEUES = getDeviceConfigInt(
                    KEY_EXTRA_RUNNING_URGENT_PROCESS_QUEUES,
                    DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES);
            MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt(
                    KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
                    DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES);
            MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS,
                    DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
            MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS,
+56 −12
Original line number Diff line number Diff line
@@ -146,6 +146,12 @@ class BroadcastProcessQueue {
     */
    private boolean mActiveViaColdStart;

    /**
     * Number of consecutive urgent broadcasts that have been dispatched
     * since the last non-urgent dispatch.
     */
    private int mActiveCountConsecutiveUrgent;

    /**
     * Count of pending broadcasts of these various flavors.
     */
@@ -546,19 +552,47 @@ class BroadcastProcessQueue {
     * {@link #isEmpty()} being false.
     */
    SomeArgs removeNextBroadcast() {
        ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
        final ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
        if (queue == mPendingUrgent) {
            mActiveCountConsecutiveUrgent++;
        } else {
            mActiveCountConsecutiveUrgent = 0;
        }
        return queue.removeFirst();
    }

    @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() {
        if (!mPendingUrgent.isEmpty()) {
            return mPendingUrgent;
        } else if (!mPending.isEmpty()) {
            return mPending;
        ArrayDeque<SomeArgs> nextUrgent = mPendingUrgent.isEmpty() ? null : mPendingUrgent;
        ArrayDeque<SomeArgs> nextNormal = null;
        if (!mPending.isEmpty()) {
            nextNormal = mPending;
        } else if (!mPendingOffload.isEmpty()) {
            return mPendingOffload;
            nextNormal = mPendingOffload;
        }
        return null;
        // nothing urgent pending, no further decisionmaking
        if (nextUrgent == null) {
            return nextNormal;
        }
        // nothing but urgent pending, also no further decisionmaking
        if (nextNormal == null) {
            return nextUrgent;
        }

        // Starvation mitigation: although we prioritize urgent broadcasts by default,
        // we allow non-urgent deliveries to make steady progress even if urgent
        // broadcasts are arriving faster than they can be dispatched.
        //
        // We do not try to defer to the next non-urgent broadcast if that broadcast
        // is ordered and still blocked on delivery to other recipients.
        final SomeArgs nextNormalArgs = nextNormal.peekFirst();
        final BroadcastRecord rNormal = (BroadcastRecord) nextNormalArgs.arg1;
        final int nextNormalIndex = nextNormalArgs.argi1;
        final BroadcastRecord rUrgent = (BroadcastRecord) nextUrgent.peekFirst().arg1;
        final boolean canTakeNormal =
                mActiveCountConsecutiveUrgent >= constants.MAX_CONSECUTIVE_URGENT_DISPATCHES
                        && rNormal.enqueueTime <= rUrgent.enqueueTime
                        && !blockedOnOrderedDispatch(rNormal, nextNormalIndex);
        return canTakeNormal ? nextNormal : nextUrgent;
    }

    /**
@@ -710,6 +744,18 @@ class BroadcastProcessQueue {
        }
    }

    private boolean blockedOnOrderedDispatch(BroadcastRecord r, int index) {
        final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];

        // We might be blocked waiting for other receivers to finish,
        // typically for an ordered broadcast or priority traunches
        if (r.terminalCount < blockedUntilTerminalCount
                && !isDeliveryStateTerminal(r.getDeliveryState(index))) {
            return true;
        }
        return false;
    }

    /**
     * Update {@link #getRunnableAt()} if it's currently invalidated.
     */
@@ -718,13 +764,11 @@ class BroadcastProcessQueue {
        if (next != null) {
            final BroadcastRecord r = (BroadcastRecord) next.arg1;
            final int index = next.argi1;
            final int blockedUntilTerminalCount = r.blockedUntilTerminalCount[index];
            final long runnableAt = r.enqueueTime;

            // We might be blocked waiting for other receivers to finish,
            // typically for an ordered broadcast or priority traunches
            if (r.terminalCount < blockedUntilTerminalCount
                    && !isDeliveryStateTerminal(r.getDeliveryState(index))) {
            // If we're specifically queued behind other ordered dispatch activity,
            // we aren't runnable yet
            if (blockedOnOrderedDispatch(r, index)) {
                mRunnableAt = Long.MAX_VALUE;
                mRunnableAtReason = REASON_BLOCKED;
                return;
+54 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.annotation.NonNull;
import android.app.Activity;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.appwidget.AppWidgetManager;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.IntentFilter;
@@ -537,6 +538,59 @@ public class BroadcastQueueModernImplTest {
        assertTrue(queue.isEmpty());
    }

    /**
     * Verify that we don't let urgent broadcasts starve delivery of non-urgent
     */
    @Test
    public void testUrgentStarvation() {
        final BroadcastOptions optInteractive = BroadcastOptions.makeBasic();
        optInteractive.setInteractive(true);

        mConstants.MAX_CONSECUTIVE_URGENT_DISPATCHES = 2;
        BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
                PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));

        // mix of broadcasts, with more than 2 fg/urgent
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_ALARM_CHANGED)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_TIME_TICK)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_LOCALE_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_APPLICATION_PREFERENCES),
                        optInteractive), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE),
                        optInteractive), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_INPUT_METHOD_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_NEW_OUTGOING_CALL),
                        optInteractive), 0);

        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_LOCALE_CHANGED, queue.getActive().intent.getAction());
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_APPLICATION_PREFERENCES, queue.getActive().intent.getAction());
        // after MAX_CONSECUTIVE_URGENT_DISPATCHES expect an ordinary one next
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_TIMEZONE_CHANGED, queue.getActive().intent.getAction());
        // and then back to prioritizing urgent ones
        queue.makeActiveNextPending();
        assertEquals(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
                queue.getActive().intent.getAction());
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_INPUT_METHOD_CHANGED, queue.getActive().intent.getAction());
        // verify the reset-count-then-resume worked too
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction());
    }

    /**
     * Verify that sending a broadcast that removes any matching pending
     * broadcasts is applied as expected.