Loading services/core/java/com/android/server/am/ActivityManagerService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -18324,7 +18324,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void waitForBroadcastBarrier(@Nullable PrintWriter pw) { public void waitForBroadcastBarrier(@Nullable PrintWriter pw) { enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); BroadcastLoopers.waitForIdle(pw); BroadcastLoopers.waitForBarrier(pw); for (BroadcastQueue queue : mBroadcastQueues) { for (BroadcastQueue queue : mBroadcastQueues) { queue.waitForBarrier(pw); queue.waitForBarrier(pw); } } services/core/java/com/android/server/am/BroadcastConstants.java +22 −0 Original line number Original line Diff line number Diff line Loading @@ -153,11 +153,26 @@ public class BroadcastConstants { "bcast_extra_running_urgent_process_queues"; "bcast_extra_running_urgent_process_queues"; private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1; 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; public int MAX_CONSECUTIVE_URGENT_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES; private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES = private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES = "bcast_max_consecutive_urgent_dispatches"; "bcast_max_consecutive_urgent_dispatches"; private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3; 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 * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts * to dispatch to a "running" process queue before we retire them back to * to dispatch to a "running" process queue before we retire them back to Loading Loading @@ -341,6 +356,9 @@ public class BroadcastConstants { MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt( MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt( KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES, KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES, DEFAULT_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, MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS, DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS); DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS); MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS, Loading Loading @@ -396,6 +414,10 @@ public class BroadcastConstants { TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println(); TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println(); pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).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_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.decreaseIndent(); pw.println(); pw.println(); } } Loading services/core/java/com/android/server/am/BroadcastLoopers.java +32 −5 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.MessageQueue; import android.os.MessageQueue; Loading @@ -30,6 +31,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.Objects; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch; import java.util.function.BiConsumer; /** /** * Collection of {@link Looper} that are known to be used for broadcast dispatch * Collection of {@link Looper} that are known to be used for broadcast dispatch Loading Loading @@ -73,19 +75,44 @@ public class BroadcastLoopers { * still in the future are ignored for the purposes of the idle test. * still in the future are ignored for the purposes of the idle test. */ */ public static void waitForIdle(@Nullable PrintWriter pw) { public static void waitForIdle(@Nullable PrintWriter pw) { waitForCondition(pw, (looper, latch) -> { final MessageQueue queue = looper.getQueue(); queue.addIdleHandler(() -> { latch.countDown(); return false; }); }); } /** * Wait for all registered {@link Looper} instances to handle currently waiting messages. * Note that {@link Message#when} still in the future are ignored for the purposes * of the idle test. */ public static void waitForBarrier(@Nullable PrintWriter pw) { waitForCondition(pw, (looper, latch) -> { (new Handler(looper)).post(() -> { latch.countDown(); }); }); } /** * Wait for all registered {@link Looper} instances to meet a certain condition. */ private static void waitForCondition(@Nullable PrintWriter pw, @NonNull BiConsumer<Looper, CountDownLatch> condition) { final CountDownLatch latch; final CountDownLatch latch; synchronized (sLoopers) { synchronized (sLoopers) { final int N = sLoopers.size(); final int N = sLoopers.size(); latch = new CountDownLatch(N); latch = new CountDownLatch(N); for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) { final MessageQueue queue = sLoopers.valueAt(i).getQueue(); final Looper looper = sLoopers.valueAt(i); final MessageQueue queue = looper.getQueue(); if (queue.isIdle()) { if (queue.isIdle()) { latch.countDown(); latch.countDown(); } else { } else { queue.addIdleHandler(() -> { condition.accept(looper, latch); latch.countDown(); return false; }); } } } } } } Loading services/core/java/com/android/server/am/BroadcastProcessQueue.java +55 −34 Original line number Original line Diff line number Diff line Loading @@ -152,6 +152,12 @@ class BroadcastProcessQueue { */ */ private int mActiveCountConsecutiveUrgent; 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. * Count of pending broadcasts of these various flavors. */ */ Loading Loading @@ -551,48 +557,63 @@ class BroadcastProcessQueue { * Will thrown an exception if there are no pending broadcasts; relies on * Will thrown an exception if there are no pending broadcasts; relies on * {@link #isEmpty()} being false. * {@link #isEmpty()} being false. */ */ SomeArgs removeNextBroadcast() { private @Nullable SomeArgs removeNextBroadcast() { final ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); final ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); if (queue == mPendingUrgent) { if (queue == mPendingUrgent) { mActiveCountConsecutiveUrgent++; mActiveCountConsecutiveUrgent++; } else { } else if (queue == mPending) { mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveNormal++; } else if (queue == mPendingOffload) { mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveNormal = 0; } } return queue.removeFirst(); return !isQueueEmpty(queue) ? queue.removeFirst() : null; } } @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() { @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() { ArrayDeque<SomeArgs> nextUrgent = mPendingUrgent.isEmpty() ? null : mPendingUrgent; final ArrayDeque<SomeArgs> nextNormal = queueForNextBroadcast( ArrayDeque<SomeArgs> nextNormal = null; mPending, mPendingOffload, if (!mPending.isEmpty()) { mActiveCountConsecutiveNormal, constants.MAX_CONSECUTIVE_NORMAL_DISPATCHES); nextNormal = mPending; final ArrayDeque<SomeArgs> nextBroadcastQueue = queueForNextBroadcast( } else if (!mPendingOffload.isEmpty()) { mPendingUrgent, nextNormal, nextNormal = mPendingOffload; mActiveCountConsecutiveUrgent, constants.MAX_CONSECUTIVE_URGENT_DISPATCHES); } return nextBroadcastQueue; // nothing urgent pending, no further decisionmaking } if (nextUrgent == null) { return nextNormal; private @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast( } @Nullable ArrayDeque<SomeArgs> highPriorityQueue, // nothing but urgent pending, also no further decisionmaking @Nullable ArrayDeque<SomeArgs> lowPriorityQueue, if (nextNormal == null) { int consecutiveHighPriorityCount, return nextUrgent; int maxHighPriorityDispatchLimit) { } // nothing high priority pending, no further decisionmaking if (isQueueEmpty(highPriorityQueue)) { // Starvation mitigation: although we prioritize urgent broadcasts by default, return lowPriorityQueue; // we allow non-urgent deliveries to make steady progress even if urgent } // broadcasts are arriving faster than they can be dispatched. // 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. // is ordered and still blocked on delivery to other recipients. final SomeArgs nextNormalArgs = nextNormal.peekFirst(); final SomeArgs nextLPArgs = lowPriorityQueue.peekFirst(); final BroadcastRecord rNormal = (BroadcastRecord) nextNormalArgs.arg1; final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1; final int nextNormalIndex = nextNormalArgs.argi1; final int nextLPRecordIndex = nextLPArgs.argi1; final BroadcastRecord rUrgent = (BroadcastRecord) nextUrgent.peekFirst().arg1; final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1; final boolean canTakeNormal = final boolean isLPQueueEligible = mActiveCountConsecutiveUrgent >= constants.MAX_CONSECUTIVE_URGENT_DISPATCHES consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit && rNormal.enqueueTime <= rUrgent.enqueueTime && nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime && !blockedOnOrderedDispatch(rNormal, nextNormalIndex); && !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex); return canTakeNormal ? nextNormal : nextUrgent; return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue; } private static boolean isQueueEmpty(@Nullable ArrayDeque<SomeArgs> queue) { return (queue == null || queue.isEmpty()); } } /** /** Loading @@ -600,13 +621,13 @@ class BroadcastProcessQueue { */ */ @Nullable SomeArgs peekNextBroadcast() { @Nullable SomeArgs peekNextBroadcast() { ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); return (queue != null) ? queue.peekFirst() : null; return !isQueueEmpty(queue) ? queue.peekFirst() : null; } } @VisibleForTesting @VisibleForTesting @Nullable BroadcastRecord peekNextBroadcastRecord() { @Nullable BroadcastRecord peekNextBroadcastRecord() { ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null; return !isQueueEmpty(queue) ? (BroadcastRecord) queue.peekFirst().arg1 : null; } } /** /** Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +67 −0 Original line number Original line Diff line number Diff line Loading @@ -591,6 +591,73 @@ public class BroadcastQueueModernImplTest { assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction()); 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 * Verify that sending a broadcast that removes any matching pending * broadcasts is applied as expected. * broadcasts is applied as expected. Loading Loading
services/core/java/com/android/server/am/ActivityManagerService.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -18324,7 +18324,7 @@ public class ActivityManagerService extends IActivityManager.Stub public void waitForBroadcastBarrier(@Nullable PrintWriter pw) { public void waitForBroadcastBarrier(@Nullable PrintWriter pw) { enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()"); BroadcastLoopers.waitForIdle(pw); BroadcastLoopers.waitForBarrier(pw); for (BroadcastQueue queue : mBroadcastQueues) { for (BroadcastQueue queue : mBroadcastQueues) { queue.waitForBarrier(pw); queue.waitForBarrier(pw); } }
services/core/java/com/android/server/am/BroadcastConstants.java +22 −0 Original line number Original line Diff line number Diff line Loading @@ -153,11 +153,26 @@ public class BroadcastConstants { "bcast_extra_running_urgent_process_queues"; "bcast_extra_running_urgent_process_queues"; private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1; 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; public int MAX_CONSECUTIVE_URGENT_DISPATCHES = DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES; private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES = private static final String KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES = "bcast_max_consecutive_urgent_dispatches"; "bcast_max_consecutive_urgent_dispatches"; private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3; 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 * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts * to dispatch to a "running" process queue before we retire them back to * to dispatch to a "running" process queue before we retire them back to Loading Loading @@ -341,6 +356,9 @@ public class BroadcastConstants { MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt( MAX_CONSECUTIVE_URGENT_DISPATCHES = getDeviceConfigInt( KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES, KEY_MAX_CONSECUTIVE_URGENT_DISPATCHES, DEFAULT_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, MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS, DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS); DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS); MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS, Loading Loading @@ -396,6 +414,10 @@ public class BroadcastConstants { TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println(); TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println(); pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).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_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.decreaseIndent(); pw.println(); pw.println(); } } Loading
services/core/java/com/android/server/am/BroadcastLoopers.java +32 −5 Original line number Original line Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.server.am; import android.annotation.NonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Nullable; import android.os.Handler; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.MessageQueue; import android.os.MessageQueue; Loading @@ -30,6 +31,7 @@ import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.Objects; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch; import java.util.function.BiConsumer; /** /** * Collection of {@link Looper} that are known to be used for broadcast dispatch * Collection of {@link Looper} that are known to be used for broadcast dispatch Loading Loading @@ -73,19 +75,44 @@ public class BroadcastLoopers { * still in the future are ignored for the purposes of the idle test. * still in the future are ignored for the purposes of the idle test. */ */ public static void waitForIdle(@Nullable PrintWriter pw) { public static void waitForIdle(@Nullable PrintWriter pw) { waitForCondition(pw, (looper, latch) -> { final MessageQueue queue = looper.getQueue(); queue.addIdleHandler(() -> { latch.countDown(); return false; }); }); } /** * Wait for all registered {@link Looper} instances to handle currently waiting messages. * Note that {@link Message#when} still in the future are ignored for the purposes * of the idle test. */ public static void waitForBarrier(@Nullable PrintWriter pw) { waitForCondition(pw, (looper, latch) -> { (new Handler(looper)).post(() -> { latch.countDown(); }); }); } /** * Wait for all registered {@link Looper} instances to meet a certain condition. */ private static void waitForCondition(@Nullable PrintWriter pw, @NonNull BiConsumer<Looper, CountDownLatch> condition) { final CountDownLatch latch; final CountDownLatch latch; synchronized (sLoopers) { synchronized (sLoopers) { final int N = sLoopers.size(); final int N = sLoopers.size(); latch = new CountDownLatch(N); latch = new CountDownLatch(N); for (int i = 0; i < N; i++) { for (int i = 0; i < N; i++) { final MessageQueue queue = sLoopers.valueAt(i).getQueue(); final Looper looper = sLoopers.valueAt(i); final MessageQueue queue = looper.getQueue(); if (queue.isIdle()) { if (queue.isIdle()) { latch.countDown(); latch.countDown(); } else { } else { queue.addIdleHandler(() -> { condition.accept(looper, latch); latch.countDown(); return false; }); } } } } } } Loading
services/core/java/com/android/server/am/BroadcastProcessQueue.java +55 −34 Original line number Original line Diff line number Diff line Loading @@ -152,6 +152,12 @@ class BroadcastProcessQueue { */ */ private int mActiveCountConsecutiveUrgent; 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. * Count of pending broadcasts of these various flavors. */ */ Loading Loading @@ -551,48 +557,63 @@ class BroadcastProcessQueue { * Will thrown an exception if there are no pending broadcasts; relies on * Will thrown an exception if there are no pending broadcasts; relies on * {@link #isEmpty()} being false. * {@link #isEmpty()} being false. */ */ SomeArgs removeNextBroadcast() { private @Nullable SomeArgs removeNextBroadcast() { final ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); final ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); if (queue == mPendingUrgent) { if (queue == mPendingUrgent) { mActiveCountConsecutiveUrgent++; mActiveCountConsecutiveUrgent++; } else { } else if (queue == mPending) { mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveNormal++; } else if (queue == mPendingOffload) { mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveUrgent = 0; mActiveCountConsecutiveNormal = 0; } } return queue.removeFirst(); return !isQueueEmpty(queue) ? queue.removeFirst() : null; } } @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() { @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast() { ArrayDeque<SomeArgs> nextUrgent = mPendingUrgent.isEmpty() ? null : mPendingUrgent; final ArrayDeque<SomeArgs> nextNormal = queueForNextBroadcast( ArrayDeque<SomeArgs> nextNormal = null; mPending, mPendingOffload, if (!mPending.isEmpty()) { mActiveCountConsecutiveNormal, constants.MAX_CONSECUTIVE_NORMAL_DISPATCHES); nextNormal = mPending; final ArrayDeque<SomeArgs> nextBroadcastQueue = queueForNextBroadcast( } else if (!mPendingOffload.isEmpty()) { mPendingUrgent, nextNormal, nextNormal = mPendingOffload; mActiveCountConsecutiveUrgent, constants.MAX_CONSECUTIVE_URGENT_DISPATCHES); } return nextBroadcastQueue; // nothing urgent pending, no further decisionmaking } if (nextUrgent == null) { return nextNormal; private @Nullable ArrayDeque<SomeArgs> queueForNextBroadcast( } @Nullable ArrayDeque<SomeArgs> highPriorityQueue, // nothing but urgent pending, also no further decisionmaking @Nullable ArrayDeque<SomeArgs> lowPriorityQueue, if (nextNormal == null) { int consecutiveHighPriorityCount, return nextUrgent; int maxHighPriorityDispatchLimit) { } // nothing high priority pending, no further decisionmaking if (isQueueEmpty(highPriorityQueue)) { // Starvation mitigation: although we prioritize urgent broadcasts by default, return lowPriorityQueue; // we allow non-urgent deliveries to make steady progress even if urgent } // broadcasts are arriving faster than they can be dispatched. // 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. // is ordered and still blocked on delivery to other recipients. final SomeArgs nextNormalArgs = nextNormal.peekFirst(); final SomeArgs nextLPArgs = lowPriorityQueue.peekFirst(); final BroadcastRecord rNormal = (BroadcastRecord) nextNormalArgs.arg1; final BroadcastRecord nextLPRecord = (BroadcastRecord) nextLPArgs.arg1; final int nextNormalIndex = nextNormalArgs.argi1; final int nextLPRecordIndex = nextLPArgs.argi1; final BroadcastRecord rUrgent = (BroadcastRecord) nextUrgent.peekFirst().arg1; final BroadcastRecord nextHPRecord = (BroadcastRecord) highPriorityQueue.peekFirst().arg1; final boolean canTakeNormal = final boolean isLPQueueEligible = mActiveCountConsecutiveUrgent >= constants.MAX_CONSECUTIVE_URGENT_DISPATCHES consecutiveHighPriorityCount >= maxHighPriorityDispatchLimit && rNormal.enqueueTime <= rUrgent.enqueueTime && nextLPRecord.enqueueTime <= nextHPRecord.enqueueTime && !blockedOnOrderedDispatch(rNormal, nextNormalIndex); && !blockedOnOrderedDispatch(nextLPRecord, nextLPRecordIndex); return canTakeNormal ? nextNormal : nextUrgent; return isLPQueueEligible ? lowPriorityQueue : highPriorityQueue; } private static boolean isQueueEmpty(@Nullable ArrayDeque<SomeArgs> queue) { return (queue == null || queue.isEmpty()); } } /** /** Loading @@ -600,13 +621,13 @@ class BroadcastProcessQueue { */ */ @Nullable SomeArgs peekNextBroadcast() { @Nullable SomeArgs peekNextBroadcast() { ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); return (queue != null) ? queue.peekFirst() : null; return !isQueueEmpty(queue) ? queue.peekFirst() : null; } } @VisibleForTesting @VisibleForTesting @Nullable BroadcastRecord peekNextBroadcastRecord() { @Nullable BroadcastRecord peekNextBroadcastRecord() { ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); ArrayDeque<SomeArgs> queue = queueForNextBroadcast(); return (queue != null) ? (BroadcastRecord) queue.peekFirst().arg1 : null; return !isQueueEmpty(queue) ? (BroadcastRecord) queue.peekFirst().arg1 : null; } } /** /** Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +67 −0 Original line number Original line Diff line number Diff line Loading @@ -591,6 +591,73 @@ public class BroadcastQueueModernImplTest { assertEquals(Intent.ACTION_ALARM_CHANGED, queue.getActive().intent.getAction()); 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 * Verify that sending a broadcast that removes any matching pending * broadcasts is applied as expected. * broadcasts is applied as expected. Loading