Merge "Increase the active broadcast delivery limit to core components." into udc-dev

This commit is contained in:
Sudheer Shanka
2023-05-09 02:17:01 +00:00
committed by Android (Google) Code Review
4 changed files with 230 additions and 99 deletions

View File

@@ -179,10 +179,38 @@ public class BroadcastConstants {
* being "runnable" to give other processes a chance to run.
*/
public int MAX_RUNNING_ACTIVE_BROADCASTS = DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS;
private static final String KEY_MAX_RUNNING_ACTIVE_BROADCASTS = "bcast_max_running_active_broadcasts";
private static final String KEY_MAX_RUNNING_ACTIVE_BROADCASTS =
"bcast_max_running_active_broadcasts";
private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS =
ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of active "blocking" broadcasts
* to dispatch to a "running" System process queue before we retire them back to
* being "runnable" to give other processes a chance to run. Here "blocking" refers to
* whether or not we are going to block on the finishReceiver() to be called before moving
* to the next broadcast.
*/
public int MAX_CORE_RUNNING_BLOCKING_BROADCASTS = DEFAULT_MAX_CORE_RUNNING_BLOCKING_BROADCASTS;
private static final String KEY_CORE_MAX_RUNNING_BLOCKING_BROADCASTS =
"bcast_max_core_running_blocking_broadcasts";
private static final int DEFAULT_MAX_CORE_RUNNING_BLOCKING_BROADCASTS =
ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of active non-"blocking" broadcasts
* to dispatch to a "running" System process queue before we retire them back to
* being "runnable" to give other processes a chance to run. Here "blocking" refers to
* whether or not we are going to block on the finishReceiver() to be called before moving
* to the next broadcast.
*/
public int MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS =
DEFAULT_MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS;
private static final String KEY_CORE_MAX_RUNNING_NON_BLOCKING_BROADCASTS =
"bcast_max_core_running_non_blocking_broadcasts";
private static final int DEFAULT_MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS =
ActivityManager.isLowRamDeviceStatic() ? 32 : 64;
/**
* For {@link BroadcastQueueModernImpl}: Maximum number of pending
* broadcasts to hold for a process before we ignore any delays that policy
@@ -369,6 +397,12 @@ public class BroadcastConstants {
DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES);
MAX_RUNNING_ACTIVE_BROADCASTS = getDeviceConfigInt(KEY_MAX_RUNNING_ACTIVE_BROADCASTS,
DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
MAX_CORE_RUNNING_BLOCKING_BROADCASTS = getDeviceConfigInt(
KEY_CORE_MAX_RUNNING_BLOCKING_BROADCASTS,
DEFAULT_MAX_CORE_RUNNING_BLOCKING_BROADCASTS);
MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS = getDeviceConfigInt(
KEY_CORE_MAX_RUNNING_NON_BLOCKING_BROADCASTS,
DEFAULT_MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS);
MAX_PENDING_BROADCASTS = getDeviceConfigInt(KEY_MAX_PENDING_BROADCASTS,
DEFAULT_MAX_PENDING_BROADCASTS);
DELAY_NORMAL_MILLIS = getDeviceConfigLong(KEY_DELAY_NORMAL_MILLIS,
@@ -418,6 +452,10 @@ public class BroadcastConstants {
pw.print(KEY_MODERN_QUEUE_ENABLED, MODERN_QUEUE_ENABLED).println();
pw.print(KEY_MAX_RUNNING_PROCESS_QUEUES, MAX_RUNNING_PROCESS_QUEUES).println();
pw.print(KEY_MAX_RUNNING_ACTIVE_BROADCASTS, MAX_RUNNING_ACTIVE_BROADCASTS).println();
pw.print(KEY_CORE_MAX_RUNNING_BLOCKING_BROADCASTS,
MAX_CORE_RUNNING_BLOCKING_BROADCASTS).println();
pw.print(KEY_CORE_MAX_RUNNING_NON_BLOCKING_BROADCASTS,
MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS).println();
pw.print(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS).println();
pw.print(KEY_DELAY_NORMAL_MILLIS,
TimeUtils.formatDuration(DELAY_NORMAL_MILLIS)).println();

View File

@@ -142,6 +142,12 @@ class BroadcastProcessQueue {
*/
private int mActiveCountSinceIdle;
/**
* Count of {@link #mActive} broadcasts with assumed delivery that have been dispatched
* since this queue was last idle.
*/
private int mActiveAssumedDeliveryCountSinceIdle;
/**
* Flag indicating that the currently active broadcast is being dispatched
* was scheduled via a cold start.
@@ -499,6 +505,14 @@ class BroadcastProcessQueue {
return mActiveCountSinceIdle;
}
/**
* Count of {@link #mActive} broadcasts with assumed delivery that have been dispatched
* since this queue was last idle.
*/
public int getActiveAssumedDeliveryCountSinceIdle() {
return mActiveAssumedDeliveryCountSinceIdle;
}
public void setActiveViaColdStart(boolean activeViaColdStart) {
mActiveViaColdStart = activeViaColdStart;
}
@@ -532,6 +546,8 @@ class BroadcastProcessQueue {
mActive = (BroadcastRecord) next.arg1;
mActiveIndex = next.argi1;
mActiveCountSinceIdle++;
mActiveAssumedDeliveryCountSinceIdle +=
(mActive.isAssumedDelivered(mActiveIndex) ? 1 : 0);
mActiveViaColdStart = false;
mActiveWasStopped = false;
next.recycle();
@@ -545,6 +561,7 @@ class BroadcastProcessQueue {
mActive = null;
mActiveIndex = 0;
mActiveCountSinceIdle = 0;
mActiveAssumedDeliveryCountSinceIdle = 0;
mActiveViaColdStart = false;
invalidateRunnableAt();
}
@@ -1369,6 +1386,7 @@ class BroadcastProcessQueue {
pw.print(" m:"); pw.print(mCountManifest);
pw.print(" csi:"); pw.print(mActiveCountSinceIdle);
pw.print(" adcsi:"); pw.print(mActiveAssumedDeliveryCountSinceIdle);
pw.print(" ccu:"); pw.print(mActiveCountConsecutiveUrgent);
pw.print(" ccn:"); pw.print(mActiveCountConsecutiveNormal);
pw.println();

View File

@@ -40,6 +40,7 @@ import static com.android.server.am.BroadcastRecord.getReceiverProcessName;
import static com.android.server.am.BroadcastRecord.getReceiverUid;
import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
import android.annotation.CheckResult;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
@@ -446,43 +447,29 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (DEBUG_BROADCAST) logv("Promoting " + queue
+ " from runnable to running; process is " + queue.app);
// Allocate this available permit and start running!
final int queueIndex = getRunningIndexOf(null);
mRunning[queueIndex] = queue;
avail--;
// Remove ourselves from linked list of runnable things
mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
// Emit all trace events for this process into a consistent track
queue.runningTraceTrackName = TAG + ".mRunning[" + queueIndex + "]";
queue.runningOomAdjusted = queue.isPendingManifest()
|| queue.isPendingOrdered()
|| queue.isPendingResultTo();
// If already warm, we can make OOM adjust request immediately;
// otherwise we need to wait until process becomes warm
promoteToRunningLocked(queue);
final boolean completed;
if (processWarm) {
notifyStartedRunning(queue);
updateOomAdj |= queue.runningOomAdjusted;
}
// If we're already warm, schedule next pending broadcast now;
// otherwise we'll wait for the cold start to circle back around
queue.makeActiveNextPending();
if (processWarm) {
queue.traceProcessRunningBegin();
scheduleReceiverWarmLocked(queue);
completed = scheduleReceiverWarmLocked(queue);
} else {
queue.traceProcessStartingBegin();
scheduleReceiverColdLocked(queue);
completed = scheduleReceiverColdLocked(queue);
}
// If we are done with delivering the broadcasts to the process, we can demote it
// from the "running" list.
if (completed) {
demoteFromRunningLocked(queue);
}
// TODO: If delivering broadcasts to a process is finished, we don't have to hold
// a slot for it.
avail--;
// Move to considering next runnable queue
queue = nextQueue;
}
// TODO: We need to update oomAdj early as this currently doesn't guarantee that the
// procState is updated correctly when the app is handling a broadcast.
if (updateOomAdj) {
mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
}
@@ -514,7 +501,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
queue.traceProcessEnd();
queue.traceProcessRunningBegin();
scheduleReceiverWarmLocked(queue);
if (scheduleReceiverWarmLocked(queue)) {
demoteFromRunningLocked(queue);
}
// We might be willing to kick off another cold start
enqueueUpdateRunningList();
@@ -558,6 +547,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (queue.isActive()) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"onApplicationCleanupLocked");
demoteFromRunningLocked(queue);
}
// Skip any pending registered receivers, since the old process
@@ -695,8 +685,13 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
* Schedule the currently active broadcast on the given queue when we know
* the process is cold. This kicks off a cold start and will eventually call
* through to {@link #scheduleReceiverWarmLocked} once it's ready.
*
* @return {@code true} if the broadcast delivery is finished and the process queue can
* be demoted from the running list. Otherwise {@code false}.
*/
private void scheduleReceiverColdLocked(@NonNull BroadcastProcessQueue queue) {
@CheckResult
@GuardedBy("mService")
private boolean scheduleReceiverColdLocked(@NonNull BroadcastProcessQueue queue) {
checkState(queue.isActive(), "isActive");
// Remember that active broadcast was scheduled via a cold start
@@ -711,12 +706,14 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
mRunningColdStart = null;
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED,
"BroadcastFilter for cold app");
return;
return true;
}
if (maybeSkipReceiver(queue, r, index)) {
final String skipReason = shouldSkipReceiver(queue, r, index);
if (skipReason != null) {
mRunningColdStart = null;
return;
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, skipReason);
return true;
}
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
@@ -742,8 +739,9 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
mRunningColdStart = null;
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"startProcessLocked failed");
return;
return true;
}
return false;
}
/**
@@ -754,38 +752,46 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
* results by calling through to {@link #finishReceiverLocked}, both in the
* case where a broadcast is handled by a remote app, and the case where the
* broadcast was finished locally without the remote app being involved.
*
* @return {@code true} if the broadcast delivery is finished and the process queue can
* be demoted from the running list. Otherwise {@code false}.
*/
@CheckResult
@GuardedBy("mService")
private void scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
private boolean scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
checkState(queue.isActive(), "isActive");
final BroadcastRecord r = queue.getActive();
final int index = queue.getActiveIndex();
final int cookie = traceBegin("scheduleReceiverWarmLocked");
while (queue.isActive()) {
final BroadcastRecord r = queue.getActive();
final int index = queue.getActiveIndex();
if (r.terminalCount == 0) {
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
}
if (r.terminalCount == 0) {
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchRealTime = SystemClock.elapsedRealtime();
r.dispatchClockTime = System.currentTimeMillis();
}
if (maybeSkipReceiver(queue, r, index)) {
return;
}
dispatchReceivers(queue, r, index);
}
final String skipReason = shouldSkipReceiver(queue, r, index);
if (skipReason == null) {
final boolean isBlockingDispatch = dispatchReceivers(queue, r, index);
if (isBlockingDispatch) {
traceEnd(cookie);
return false;
}
} else {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, skipReason);
}
/**
* Examine a receiver and possibly skip it. The method returns true if the receiver is
* skipped (and therefore no more work is required).
*/
private boolean maybeSkipReceiver(@NonNull BroadcastProcessQueue queue,
@NonNull BroadcastRecord r, int index) {
final String reason = shouldSkipReceiver(queue, r, index);
if (reason != null) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_SKIPPED, reason);
return true;
if (shouldRetire(queue)) {
break;
}
// We're on a roll; move onto the next broadcast for this process
queue.makeActiveNextPending();
}
return false;
traceEnd(cookie);
return true;
}
/**
@@ -825,25 +831,22 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
return null;
}
/**
* Return true if this receiver should be assumed to have been delivered.
*/
private boolean isAssumedDelivered(BroadcastRecord r, int index) {
return (r.receivers.get(index) instanceof BroadcastFilter) && !r.ordered
&& (r.resultTo == null);
}
/**
* A receiver is about to be dispatched. Start ANR timers, if necessary.
*
* @return {@code true} if this a blocking delivery. That is, we are going to block on the
* finishReceiver() to be called before moving to the next broadcast. Otherwise,
* {@code false}.
*/
private void dispatchReceivers(@NonNull BroadcastProcessQueue queue,
@CheckResult
private boolean dispatchReceivers(@NonNull BroadcastProcessQueue queue,
@NonNull BroadcastRecord r, int index) {
final ProcessRecord app = queue.app;
final Object receiver = r.receivers.get(index);
// Skip ANR tracking early during boot, when requested, or when we
// immediately assume delivery success
final boolean assumeDelivered = isAssumedDelivered(r, index);
final boolean assumeDelivered = r.isAssumedDelivered(index);
if (mService.mProcessesReady && !r.timeoutExempt && !assumeDelivered) {
queue.lastCpuDelayTime = queue.app.getCpuDelayTime();
@@ -898,6 +901,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
if (assumeDelivered) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_DELIVERED,
"assuming delivered");
return false;
}
} else {
notifyScheduleReceiver(app, r, (ResolveInfo) receiver);
@@ -908,17 +912,21 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
r.shareIdentity ? r.callingUid : Process.INVALID_UID,
r.shareIdentity ? r.callerPackage : null);
}
return true;
} catch (RemoteException e) {
final String msg = "Failed to schedule " + r + " to " + receiver
+ " via " + app + ": " + e;
logw(msg);
app.killLocked("Can't deliver broadcast", ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_UNDELIVERED_BROADCAST, true);
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE, "remote app");
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"remote app");
return false;
}
} else {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_FAILURE,
"missing IApplicationThread");
return false;
}
}
@@ -989,6 +997,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
private void deliveryTimeoutHardLocked(@NonNull BroadcastProcessQueue queue) {
finishReceiverActiveLocked(queue, BroadcastRecord.DELIVERY_TIMEOUT,
"deliveryTimeoutHardLocked");
demoteFromRunningLocked(queue);
}
@Override
@@ -1015,7 +1024,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// To ensure that "beyond" high-water marks are updated in a monotonic
// way, we finish this receiver before possibly skipping any remaining
// aborted receivers
final boolean res = finishReceiverActiveLocked(queue,
finishReceiverActiveLocked(queue,
BroadcastRecord.DELIVERY_DELIVERED, "remote app");
// When the caller aborted an ordered broadcast, we mark all
@@ -1027,30 +1036,52 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
}
}
return res;
if (shouldRetire(queue)) {
demoteFromRunningLocked(queue);
return true;
}
// We're on a roll; move onto the next broadcast for this process
queue.makeActiveNextPending();
if (scheduleReceiverWarmLocked(queue)) {
demoteFromRunningLocked(queue);
return true;
}
return false;
}
/**
* Return true if there are more broadcasts in the queue and the queue is runnable.
* Return true if there are no more broadcasts in the queue or if the queue is not runnable.
*/
private boolean shouldContinueScheduling(@NonNull BroadcastProcessQueue queue) {
private boolean shouldRetire(@NonNull BroadcastProcessQueue queue) {
// If we've made reasonable progress, periodically retire ourselves to
// avoid starvation of other processes and stack overflow when a
// broadcast is immediately finished without waiting
final boolean shouldRetire =
(queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
final boolean shouldRetire;
if (UserHandle.isCore(queue.uid)) {
final int nonBlockingDeliveryCount = queue.getActiveAssumedDeliveryCountSinceIdle();
final int blockingDeliveryCount = (queue.getActiveCountSinceIdle()
- queue.getActiveAssumedDeliveryCountSinceIdle());
shouldRetire = (blockingDeliveryCount
>= mConstants.MAX_CORE_RUNNING_BLOCKING_BROADCASTS) || (nonBlockingDeliveryCount
>= mConstants.MAX_CORE_RUNNING_NON_BLOCKING_BROADCASTS);
} else {
shouldRetire =
(queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
}
return queue.isRunnable() && queue.isProcessWarm() && !shouldRetire;
return !queue.isRunnable() || !queue.isProcessWarm() || shouldRetire;
}
/**
* Terminate all active broadcasts on the queue.
*/
private boolean finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,
private void finishReceiverActiveLocked(@NonNull BroadcastProcessQueue queue,
@DeliveryState int deliveryState, @NonNull String reason) {
if (!queue.isActive()) {
logw("Ignoring finish; no active broadcast for " + queue);
return false;
return;
}
final int cookie = traceBegin("finishReceiver");
@@ -1077,27 +1108,63 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
// Given that a receiver just finished, check if the "waitingFor" conditions are met.
checkAndRemoveWaitingFor();
final boolean res = shouldContinueScheduling(queue);
if (res) {
// We're on a roll; move onto the next broadcast for this process
queue.makeActiveNextPending();
scheduleReceiverWarmLocked(queue);
} else {
// We've drained running broadcasts; maybe move back to runnable
queue.makeActiveIdle();
queue.traceProcessEnd();
final int queueIndex = getRunningIndexOf(queue);
mRunning[queueIndex] = null;
updateRunnableList(queue);
enqueueUpdateRunningList();
// Tell other OS components that app is not actively running, giving
// a chance to update OOM adjustment
notifyStoppedRunning(queue);
}
traceEnd(cookie);
return res;
}
/**
* Promote a process to the "running" list.
*/
@GuardedBy("mService")
private void promoteToRunningLocked(@NonNull BroadcastProcessQueue queue) {
// Allocate this available permit and start running!
final int queueIndex = getRunningIndexOf(null);
mRunning[queueIndex] = queue;
// Remove ourselves from linked list of runnable things
mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
// Emit all trace events for this process into a consistent track
queue.runningTraceTrackName = TAG + ".mRunning[" + queueIndex + "]";
queue.runningOomAdjusted = queue.isPendingManifest()
|| queue.isPendingOrdered()
|| queue.isPendingResultTo();
// If already warm, we can make OOM adjust request immediately;
// otherwise we need to wait until process becomes warm
final boolean processWarm = queue.isProcessWarm();
if (processWarm) {
notifyStartedRunning(queue);
}
// If we're already warm, schedule next pending broadcast now;
// otherwise we'll wait for the cold start to circle back around
queue.makeActiveNextPending();
if (processWarm) {
queue.traceProcessRunningBegin();
} else {
queue.traceProcessStartingBegin();
}
}
/**
* Demote a process from the "running" list.
*/
@GuardedBy("mService")
private void demoteFromRunningLocked(@NonNull BroadcastProcessQueue queue) {
final int cookie = traceBegin("demoteFromRunning");
// We've drained running broadcasts; maybe move back to runnable
queue.makeActiveIdle();
queue.traceProcessEnd();
final int queueIndex = getRunningIndexOf(queue);
mRunning[queueIndex] = null;
updateRunnableList(queue);
enqueueUpdateRunningList();
// Tell other OS components that app is not actively running, giving
// a chance to update OOM adjustment
notifyStoppedRunning(queue);
traceEnd(cookie);
}
/**

View File

@@ -237,6 +237,14 @@ final class BroadcastRecord extends Binder {
}
}
/**
* Return true if this receiver should be assumed to have been delivered.
*/
boolean isAssumedDelivered(int index) {
return (receivers.get(index) instanceof BroadcastFilter) && !ordered
&& (resultTo == null);
}
ProcessRecord curApp; // hosting application of current receiver.
ComponentName curComponent; // the receiver class that is currently running.
ActivityInfo curReceiver; // the manifest receiver that is currently running.