diff options
| author | 2022-08-18 18:10:04 -0700 | |
|---|---|---|
| committer | 2022-08-29 11:36:11 -0700 | |
| commit | 71249db69fda3f195dadd43a964ddb2ecacb21b0 (patch) | |
| tree | 7976a0ea9fbd1a67923f57739bbf820c707e7719 | |
| parent | 21d8b0c7dae25a5eb9788f826fd642531e5cdb73 (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.java | 31 | ||||
| -rw-r--r-- | services/core/java/com/android/server/am/BroadcastDispatcher.java | 98 |
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); } |