Loading services/core/java/com/android/server/am/BroadcastConstants.java +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading services/core/java/com/android/server/am/BroadcastProcessQueue.java +56 −12 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; } /** Loading Loading @@ -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. */ Loading @@ -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; Loading services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading
services/core/java/com/android/server/am/BroadcastConstants.java +8 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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, Loading
services/core/java/com/android/server/am/BroadcastProcessQueue.java +56 −12 Original line number Diff line number Diff line Loading @@ -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. */ Loading Loading @@ -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; } /** Loading Loading @@ -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. */ Loading @@ -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; Loading
services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +54 −0 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading