summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Christopher Tate <ctate@google.com> 2022-08-18 18:10:04 -0700
committer Christopher Tate <ctate@google.com> 2022-08-29 11:36:11 -0700
commit71249db69fda3f195dadd43a964ddb2ecacb21b0 (patch)
tree7976a0ea9fbd1a67923f57739bbf820c707e7719
parent21d8b0c7dae25a5eb9788f826fd642531e5cdb73 (diff)
Implement alarm precedence for broadcast delivery
Rather than queueing alarm-triggered broadcasts with the rest of the outbound ordered broadcasts, we now give them delivery precedence over the general queue, to minimize parasitic execution under the alarm wakelock. A new DeviceConfig flag can optionally be used to override the platform default and select between immediate alarm delivery and the previous behavior. Bug: 161537193 Test: atest CtsAlarmManagerTestCases Change-Id: Ic18654427a07831dfbe7ec5edace36999cf9973f
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerConstants.java31
-rw-r--r--services/core/java/com/android/server/am/BroadcastDispatcher.java98
2 files changed, 104 insertions, 25 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index 13a13f2f4b3c..5e7d814b241b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -146,6 +146,7 @@ final class ActivityManagerConstants extends ContentObserver {
static final String KEY_NETWORK_ACCESS_TIMEOUT_MS = "network_access_timeout_ms";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
+ private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true;
private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000;
private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000;
private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000;
@@ -325,6 +326,14 @@ final class ActivityManagerConstants extends ContentObserver {
*/
private static final String KEY_PROCESS_KILL_TIMEOUT = "process_kill_timeout";
+ /**
+ * {@code true} to send in-flight alarm broadcasts ahead of non-alarms; {@code false}
+ * to queue alarm broadcasts identically to non-alarms [i.e. the pre-U behavior]; or
+ * {@code null} or empty string in order to fall back to whatever the build-time default
+ * was for the device.
+ */
+ private static final String KEY_PRIORITIZE_ALARM_BROADCASTS = "prioritize_alarm_broadcasts";
+
private static final String KEY_DEFER_BOOT_COMPLETED_BROADCAST =
"defer_boot_completed_broadcast";
@@ -667,6 +676,12 @@ final class ActivityManagerConstants extends ContentObserver {
DEFAULT_DEFER_BOOT_COMPLETED_BROADCAST;
/**
+ * Whether alarm broadcasts are delivered immediately, or queued along with the rest
+ * of the pending ordered broadcasts.
+ */
+ volatile boolean mPrioritizeAlarmBroadcasts = DEFAULT_PRIORITIZE_ALARM_BROADCASTS;
+
+ /**
* How long the Context.startForegroundService() grace period is to get around to
* calling Service.startForeground() before we generate ANR.
*/
@@ -977,6 +992,9 @@ final class ActivityManagerConstants extends ContentObserver {
case KEY_PROCESS_KILL_TIMEOUT:
updateProcessKillTimeout();
break;
+ case KEY_PRIORITIZE_ALARM_BROADCASTS:
+ updatePrioritizeAlarmBroadcasts();
+ break;
case KEY_DEFER_BOOT_COMPLETED_BROADCAST:
updateDeferBootCompletedBroadcast();
break;
@@ -1446,6 +1464,17 @@ final class ActivityManagerConstants extends ContentObserver {
}
}
+ private void updatePrioritizeAlarmBroadcasts() {
+ // Flag value can be something that evaluates to `true` or `false`,
+ // or empty/null. If it's empty/null, the platform default is used.
+ final String flag = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_PRIORITIZE_ALARM_BROADCASTS,
+ "");
+ mPrioritizeAlarmBroadcasts = TextUtils.isEmpty(flag)
+ ? DEFAULT_PRIORITIZE_ALARM_BROADCASTS
+ : Boolean.parseBoolean(flag);
+ }
private void updateDeferBootCompletedBroadcast() {
mDeferBootCompletedBroadcast = DeviceConfig.getInt(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
@@ -1786,6 +1815,8 @@ final class ActivityManagerConstants extends ContentObserver {
pw.print("="); pw.println(mComponentAliasOverrides);
pw.print(" "); pw.print(KEY_DEFER_BOOT_COMPLETED_BROADCAST);
pw.print("="); pw.println(mDeferBootCompletedBroadcast);
+ pw.print(" "); pw.print(KEY_PRIORITIZE_ALARM_BROADCASTS);
+ pw.print("="); pw.println(mPrioritizeAlarmBroadcasts);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_UNTIL_BOOT_COMPLETED);
pw.print("="); pw.println(mNoKillCachedProcessesUntilBootCompleted);
pw.print(" "); pw.print(KEY_NO_KILL_CACHED_PROCESSES_POST_BOOT_COMPLETED_DURATION_MILLIS);
diff --git a/services/core/java/com/android/server/am/BroadcastDispatcher.java b/services/core/java/com/android/server/am/BroadcastDispatcher.java
index e9a36e05c6c1..4c443433d396 100644
--- a/services/core/java/com/android/server/am/BroadcastDispatcher.java
+++ b/services/core/java/com/android/server/am/BroadcastDispatcher.java
@@ -181,7 +181,7 @@ public class BroadcastDispatcher {
for (int i = 0; i < numEntries; i++) {
if (recipientUid == mDeferredBroadcasts.get(i).uid) {
Deferrals d = mDeferredBroadcasts.remove(i);
- mAlarmBroadcasts.add(d);
+ mAlarmDeferrals.add(d);
break;
}
}
@@ -201,10 +201,10 @@ public class BroadcastDispatcher {
// No longer an alarm target, so resume ordinary deferral policy
if (newCount <= 0) {
- final int numEntries = mAlarmBroadcasts.size();
+ final int numEntries = mAlarmDeferrals.size();
for (int i = 0; i < numEntries; i++) {
- if (recipientUid == mAlarmBroadcasts.get(i).uid) {
- Deferrals d = mAlarmBroadcasts.remove(i);
+ if (recipientUid == mAlarmDeferrals.get(i).uid) {
+ Deferrals d = mAlarmDeferrals.remove(i);
insertLocked(mDeferredBroadcasts, d);
break;
}
@@ -234,7 +234,13 @@ public class BroadcastDispatcher {
// General deferrals not holding up alarms
private final ArrayList<Deferrals> mDeferredBroadcasts = new ArrayList<>();
// Deferrals that *are* holding up alarms; ordered by alarm dispatch time
- private final ArrayList<Deferrals> mAlarmBroadcasts = new ArrayList<>();
+ private final ArrayList<Deferrals> mAlarmDeferrals = new ArrayList<>();
+ // Under the "deliver alarm broadcasts immediately" policy, the queue of
+ // upcoming alarm broadcasts. These are always delivered first - if the
+ // policy is changed on the fly from immediate-alarm-delivery to the previous
+ // in-order-queueing behavior, pending immediate alarm deliveries will drain
+ // and then the behavior settle into the pre-U semantics.
+ private final ArrayList<BroadcastRecord> mAlarmQueue = new ArrayList<>();
// Next outbound broadcast, established by getNextBroadcastLocked()
private BroadcastRecord mCurrentBroadcast;
@@ -528,8 +534,9 @@ public class BroadcastDispatcher {
synchronized (mLock) {
return mCurrentBroadcast == null
&& mOrderedBroadcasts.isEmpty()
+ && mAlarmQueue.isEmpty()
&& isDeferralsListEmpty(mDeferredBroadcasts)
- && isDeferralsListEmpty(mAlarmBroadcasts);
+ && isDeferralsListEmpty(mAlarmDeferrals);
}
}
@@ -557,7 +564,13 @@ public class BroadcastDispatcher {
}
sb.append(mOrderedBroadcasts.size());
sb.append(" ordered");
- int n = pendingInDeferralsList(mAlarmBroadcasts);
+ int n = mAlarmQueue.size();
+ if (n > 0) {
+ sb.append(", ");
+ sb.append(n);
+ sb.append(" alarms");
+ }
+ n = pendingInDeferralsList(mAlarmDeferrals);
if (n > 0) {
sb.append(", ");
sb.append(n);
@@ -592,8 +605,16 @@ public class BroadcastDispatcher {
// ----------------------------------
// BroadcastQueue operation support
void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
+ final ArrayList<BroadcastRecord> queue =
+ (r.alarm && mQueue.mService.mConstants.mPrioritizeAlarmBroadcasts)
+ ? mAlarmQueue
+ : mOrderedBroadcasts;
+
if (r.receivers == null || r.receivers.isEmpty()) {
- mOrderedBroadcasts.add(r);
+ // Fast no-op path for broadcasts that won't actually be dispatched to
+ // receivers - we still need to handle completion callbacks and historical
+ // records, but we don't need to consider the fancy cases.
+ queue.add(r);
return;
}
@@ -622,7 +643,8 @@ public class BroadcastDispatcher {
return;
}
} else {
- mOrderedBroadcasts.add(r);
+ // Ordinary broadcast, so put it on the appropriate queue and carry on
+ queue.add(r);
}
}
@@ -652,10 +674,13 @@ public class BroadcastDispatcher {
BroadcastRecord replaceBroadcastLocked(BroadcastRecord r, String typeForLogging) {
// Simple case, in the ordinary queue.
BroadcastRecord old = replaceBroadcastLocked(mOrderedBroadcasts, r, typeForLogging);
-
+ // ... or possibly in the simple alarm queue
+ if (old == null) {
+ old = replaceBroadcastLocked(mAlarmQueue, r, typeForLogging);
+ }
// If we didn't find it, less-simple: in a deferral queue?
if (old == null) {
- old = replaceDeferredBroadcastLocked(mAlarmBroadcasts, r, typeForLogging);
+ old = replaceDeferredBroadcastLocked(mAlarmDeferrals, r, typeForLogging);
}
if (old == null) {
old = replaceDeferredBroadcastLocked(mDeferredBroadcasts, r, typeForLogging);
@@ -706,6 +731,10 @@ public class BroadcastDispatcher {
boolean didSomething = cleanupBroadcastListDisabledReceiversLocked(mOrderedBroadcasts,
packageName, filterByClasses, userId, doit);
if (doit || !didSomething) {
+ didSomething = cleanupBroadcastListDisabledReceiversLocked(mAlarmQueue,
+ packageName, filterByClasses, userId, doit);
+ }
+ if (doit || !didSomething) {
ArrayList<BroadcastRecord> lockedBootCompletedBroadcasts = new ArrayList<>();
for (int u = 0, usize = mUser2Deferred.size(); u < usize; u++) {
SparseArray<BroadcastRecord> brs =
@@ -731,7 +760,7 @@ public class BroadcastDispatcher {
packageName, filterByClasses, userId, doit);
}
if (doit || !didSomething) {
- didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmBroadcasts,
+ didSomething |= cleanupDeferralsListDisabledReceiversLocked(mAlarmDeferrals,
packageName, filterByClasses, userId, doit);
}
if (doit || !didSomething) {
@@ -781,12 +810,15 @@ public class BroadcastDispatcher {
if (mCurrentBroadcast != null) {
mCurrentBroadcast.dumpDebug(proto, fieldId);
}
- for (Deferrals d : mAlarmBroadcasts) {
+ for (Deferrals d : mAlarmDeferrals) {
d.dumpDebug(proto, fieldId);
}
for (BroadcastRecord br : mOrderedBroadcasts) {
br.dumpDebug(proto, fieldId);
}
+ for (BroadcastRecord br : mAlarmQueue) {
+ br.dumpDebug(proto, fieldId);
+ }
for (Deferrals d : mDeferredBroadcasts) {
d.dumpDebug(proto, fieldId);
}
@@ -816,23 +848,33 @@ public class BroadcastDispatcher {
return mCurrentBroadcast;
}
- final boolean someQueued = !mOrderedBroadcasts.isEmpty();
-
BroadcastRecord next = null;
+ // Alarms in flight take precedence over everything else. This queue
+ // will be non-empty only when the relevant policy is in force, but if
+ // policy has changed on the fly we still need to drain this before we
+ // settle into the legacy behavior.
+ if (!mAlarmQueue.isEmpty()) {
+ next = mAlarmQueue.remove(0);
+ }
+
+ // Next in precedence are deferred BOOT_COMPLETED broadcasts
if (next == null) {
next = dequeueDeferredBootCompletedBroadcast();
}
- if (next == null && !mAlarmBroadcasts.isEmpty()) {
- next = popLocked(mAlarmBroadcasts);
+ // Alarm-related deferrals are next in precedence...
+ if (next == null && !mAlarmDeferrals.isEmpty()) {
+ next = popLocked(mAlarmDeferrals);
if (DEBUG_BROADCAST_DEFERRAL && next != null) {
Slog.i(TAG, "Next broadcast from alarm targets: " + next);
}
}
+ final boolean someQueued = !mOrderedBroadcasts.isEmpty();
+
if (next == null && !mDeferredBroadcasts.isEmpty()) {
- // We're going to deliver either:
+ // A this point we're going to deliver either:
// 1. the next "overdue" deferral; or
// 2. the next ordinary ordered broadcast; *or*
// 3. the next not-yet-overdue deferral.
@@ -937,7 +979,7 @@ public class BroadcastDispatcher {
scheduleDeferralCheckLocked(true);
} else {
// alarm-related: strict order-encountered
- mAlarmBroadcasts.add(d);
+ mAlarmDeferrals.add(d);
}
} else {
// We're already deferring, but something was slow again. Reset the
@@ -999,7 +1041,7 @@ public class BroadcastDispatcher {
* immediately deliverable. Used by the wait-for-broadcast-idle mechanism.
*/
public void cancelDeferralsLocked() {
- zeroDeferralTimes(mAlarmBroadcasts);
+ zeroDeferralTimes(mAlarmDeferrals);
zeroDeferralTimes(mDeferredBroadcasts);
}
@@ -1023,7 +1065,7 @@ public class BroadcastDispatcher {
Deferrals d = findUidLocked(uid, mDeferredBroadcasts);
// ...but if not there, also check alarm-prioritized deferrals
if (d == null) {
- d = findUidLocked(uid, mAlarmBroadcasts);
+ d = findUidLocked(uid, mAlarmDeferrals);
}
return d;
}
@@ -1035,7 +1077,7 @@ public class BroadcastDispatcher {
private boolean removeDeferral(Deferrals d) {
boolean didRemove = mDeferredBroadcasts.remove(d);
if (!didRemove) {
- didRemove = mAlarmBroadcasts.remove(d);
+ didRemove = mAlarmDeferrals.remove(d);
}
return didRemove;
}
@@ -1103,14 +1145,20 @@ public class BroadcastDispatcher {
} else {
pw.println(" (null)");
}
+ printed |= dumper.didPrint();
+
+ dumper.setHeading("Active alarm broadcasts");
+ dumper.setLabel("Active Alarm Broadcast");
+ for (BroadcastRecord br : mAlarmQueue) {
+ dumper.dump(br);
+ }
+ printed |= dumper.didPrint();
dumper.setHeading("Active ordered broadcasts");
dumper.setLabel("Active Ordered Broadcast");
- for (Deferrals d : mAlarmBroadcasts) {
+ for (Deferrals d : mAlarmDeferrals) {
d.dumpLocked(dumper);
}
- printed |= dumper.didPrint();
-
for (BroadcastRecord br : mOrderedBroadcasts) {
dumper.dump(br);
}