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

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

Don't let offload broadcasts to be starved.

Similar to how we are making sure urgent broadcasts don't
starve normal broadcasts, we need to make sure offload
broadcasts don't get starved by urgent and normal broadcasts.

Bug: 260158381
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: Ibd70ef40598178673d6731efa06b1f57fa90970c
parent 75cb4898
Loading
Loading
Loading
Loading
+22 −0
Original line number Diff line number Diff line
@@ -153,11 +153,26 @@ public class BroadcastConstants {
            "bcast_extra_running_urgent_process_queues";
    private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1;

    /**
     * For {@link BroadcastQueueModernImpl}: Maximum number of consecutive urgent
     * broadcast dispatches allowed before letting broadcasts in lower priority queue
     * to be scheduled in order to avoid starvation.
     */
    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 consecutive normal
     * broadcast dispatches allowed before letting broadcasts in lower priority queue
     * to be scheduled in order to avoid starvation.
     */
    public int MAX_CONSECUTIVE_NORMAL_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES;
    private static final String KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES =
            "bcast_max_consecutive_normal_dispatches";
    private static final int DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES = 10;

    /**
     * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
     * to dispatch to a "running" process queue before we retire them back to
@@ -341,6 +356,9 @@ public class BroadcastConstants {
            MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt(
                    KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
                    DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES);
            MAX_CONSECUTIVE_NORMAL_DISPATCHES = getDeviceConfigInt(
                    KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
                    DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES);
            MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS,
                    DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
            MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS,
@@ -396,6 +414,10 @@ public class BroadcastConstants {
                    TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
            pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
            pw.print(KEY_MAX_HISTORY_SUMMARY_SIZE, MAX_HISTORY_SUMMARY_SIZE).println();
            pw.print(KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES,
                    MAX_CONSECUTIVE_URGENT_DISPATCHES).println();
            pw.print(KEY_MAX_CONSECUTIVE_NORMAL_DISPATCHES,
                    MAX_CONSECUTIVE_NORMAL_DISPATCHES).println();
            pw.decreaseIndent();
            pw.println();
        }
+55 −34
Original line number Diff line number Diff line
@@ -152,6 +152,12 @@ class BroadcastProcessQueue {
     */
    private int mActiveCountConsecutiveUrgent;

    /**
     * Number of consecutive normal broadcasts that have been dispatched
     * since the last offload dispatch.
     */
    private int mActiveCountConsecutiveNormal;

    /**
     * Count of pending broadcasts of these various flavors.
     */
@@ -551,48 +557,63 @@ class BroadcastProcessQueue {
     * Will thrown an exception if there are no pending broadcasts; relies on
     * {@link #isEmpty()} being false.
     */
    SomeArgs removeNextBroadcast() {
    private @Nullable SomeArgs removeNextBroadcast() {
        final ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
        if (queue == mPendingUrgent) {
            mActiveCountConsecutiveUrgent++;
        } else {
        } else if (queue == mPending) {
            mActiveCountConsecutiveUrgent = 0;
            mActiveCountConsecutiveNormal++;
        } else if (queue == mPendingOffload) {
            mActiveCountConsecutiveUrgent = 0;
            mActiveCountConsecutiveNormal = 0;
        }
        return queue.removeFirst();
        return !isQueueEmpty(queue) ? queue.removeFirst() : null;
    }

    @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() {
        ArrayDeque<SomeArgs> nextUrgent = mPendingUrgent.isEmpty() ? null : mPendingUrgent;
        ArrayDeque<SomeArgs> nextNormal = null;
        if (!mPending.isEmpty()) {
            nextNormal = mPending;
        } else if (!mPendingOffload.isEmpty()) {
            nextNormal = mPendingOffload;
        }
        // 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.
        final ArrayDeque<SomeArgs> nextNormal = queueForNextBroadcast(
                mPending, mPendingOffload,
                mActiveCountConsecutiveNormal, constants.MAX_CONSECUTIVE_NORMAL_DISPATCHES);
        final ArrayDeque<SomeArgs> nextBroadcastQueue = queueForNextBroadcast(
                mPendingUrgent, nextNormal,
                mActiveCountConsecutiveUrgent, constants.MAX_CONSECUTIVE_URGENT_DISPATCHES);
        return nextBroadcastQueue;
    }

    private @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast(
            @Nullable ArrayDeque<SomeArgs> highPriorityQueue,
            @Nullable ArrayDeque<SomeArgs> lowPriorityQueue,
            int consecutiveHighPriorityCount,
            int maxHighPriorityDispatchLimit) {
        // nothing high priority pending, no further decisionmaking
        if (isQueueEmpty(highPriorityQueue)) {
            return lowPriorityQueue;
        }
        // nothing but high priority pending, also no further decisionmaking
        if (isQueueEmpty(lowPriorityQueue)) {
            return highPriorityQueue;
        }

        // Starvation mitigation: although we prioritize high priority queues by default,
        // we allow low priority queues to make steady progress even if broadcasts in
        // high priority queue are arriving faster than they can be dispatched.
        //
        // We do not try to defer to the next non-urgent broadcast if that broadcast
        // We do not try to defer to the next broadcast in low priority queues 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;
        final SomeArgs nextLPArgs = lowPriorityQueue.peekFirst();
        final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1;
        final int nextLPRecordIndex = nextLPArgs.argi1;
        final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1;
        final boolean isLPQueueEligible =
                consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit
                        && nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime
                        && !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex);
        return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue;
    }

    private static boolean isQueueEmpty(@Nullable ArrayDeque<SomeArgs> queue) {
        return (queue == null || queue.isEmpty());
    }

    /**
@@ -600,13 +621,13 @@ class BroadcastProcessQueue {
     */
    @Nullable SomeArgs peekNextBroadcast() {
        ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
        return (queue != null) ? queue.peekFirst() : null;
        return !isQueueEmpty(queue) ? queue.peekFirst() : null;
    }

    @VisibleForTesting
    @Nullable BroadcastRecord peekNextBroadcastRecord() {
        ArrayDeque<SomeArgs> queue = queueForNextBroadcast();
        return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null;
        return !isQueueEmpty(queue) ? (BroadcastRecord) queue.peekFirst().arg1 : null;
    }

    /**
+67 −0
Original line number Diff line number Diff line
@@ -591,6 +591,73 @@ public class BroadcastQueueModernImplTest {
        assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction());
    }

    /**
     * Verify that offload broadcasts are not starved because of broadcasts in higher priority
     * queues.
     */
    @Test
    public void testOffloadStarvation() {
        final BroadcastOptions optInteractive = BroadcastOptions.makeBasic();
        optInteractive.setInteractive(true);

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

        // mix of broadcasts, with more than 2 normal
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_BOOT_COMPLETED)
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_TIMEZONE_CHANGED)), 0);
        queue.enqueueOrReplaceBroadcast(
                makeBroadcastRecord(new Intent(Intent.ACTION_PACKAGE_CHANGED)
                        .addFlags(Intent.FLAG_RECEIVER_OFFLOAD)), 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());
        // 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(Intent.ACTION_APPLICATION_PREFERENCES, queue.getActive().intent.getAction());
        // after MAX_CONSECUTIVE_URGENT_DISPATCHES, again an ordinary one next
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction());
        // and then back to prioritizing urgent ones
        queue.makeActiveNextPending();
        assertEquals(AppWidgetManager.ACTION_APPWIDGET_UPDATE,
                queue.getActive().intent.getAction());
        // after MAX_CONSECUTIVE_URGENT_DISPATCHES and MAX_CONSECUTIVE_NORMAL_DISPATCHES,
        // expect an offload one
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_BOOT_COMPLETED, queue.getActive().intent.getAction());
        // and then back to prioritizing urgent ones
        queue.makeActiveNextPending();
        assertEquals(Intent.ACTION_INPUT_METHOD_CHANGED, queue.getActive().intent.getAction());
    }

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