From 1590f1ee7b7b7962f08e8cc29b9891b2ad124c9b Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Thu, 2 Oct 2014 17:27:57 -0700 Subject: [PATCH] Fix alarm delivery-order sorting We also refine the order of delivery within any given package. Now, we identify which apps have wakeup alarms being delivered in the current pass, and deliver all of that app's alarms before moving on to alarm delivery to apps who are only receiving non-wakeup alarms in the current delivery pass. The TIME_TICK alarm is also hoisted to the start of the current delivery pass if present. Bug 17778686 Change-Id: I6306a00fe657787a77d0254c0807ac51e810fdcf --- .../android/server/AlarmManagerService.java | 79 +++++++++++++++++-- 1 file changed, 73 insertions(+), 6 deletions(-) diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 47396bda1be10..8b524ddab458c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -58,6 +58,7 @@ import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; import java.util.TimeZone; @@ -141,6 +142,25 @@ class AlarmManagerService extends SystemService { private final SparseArray mHandlerSparseAlarmClockArray = new SparseArray<>(); + // Alarm delivery ordering bookkeeping + static final int PRIO_TICK = 0; + static final int PRIO_WAKEUP = 1; + static final int PRIO_NORMAL = 2; + + class PriorityClass { + int seq; + int priority; + + PriorityClass() { + seq = mCurrentSeq - 1; + priority = PRIO_NORMAL; + } + } + + final HashMap mPriorities = + new HashMap(); + int mCurrentSeq = 0; + class WakeupEvent { public long when; public int uid; @@ -356,22 +376,62 @@ class AlarmManagerService extends SystemService { final Comparator mAlarmDispatchComparator = new Comparator() { @Override public int compare(Alarm lhs, Alarm rhs) { - if ((!lhs.operation.getCreatorPackage().equals(rhs.operation.getCreatorPackage())) - && lhs.wakeup != rhs.wakeup) { - // We want to put wakeup alarms before non-wakeup alarms, since they are - // the things that drive most activity in the alarm manager. However, - // alarms from the same package should always be ordered strictly by time. - return lhs.wakeup ? -1 : 1; + // priority class trumps everything. TICK < WAKEUP < NORMAL + if (lhs.priorityClass.priority < rhs.priorityClass.priority) { + return -1; + } else if (lhs.priorityClass.priority > rhs.priorityClass.priority) { + return 1; } + + // within each class, sort by nominal delivery time if (lhs.whenElapsed < rhs.whenElapsed) { return -1; } else if (lhs.whenElapsed > rhs.whenElapsed) { return 1; } + + // same priority class + same target delivery time return 0; } }; + void calculateDeliveryPriorities(ArrayList alarms) { + final int N = alarms.size(); + for (int i = 0; i < N; i++) { + Alarm a = alarms.get(i); + + final int alarmPrio; + if (Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) { + alarmPrio = PRIO_TICK; + } else if (a.wakeup) { + alarmPrio = PRIO_WAKEUP; + } else { + alarmPrio = PRIO_NORMAL; + } + + PriorityClass packagePrio = a.priorityClass; + if (packagePrio == null) packagePrio = mPriorities.get(a.operation.getCreatorPackage()); + if (packagePrio == null) { + packagePrio = a.priorityClass = new PriorityClass(); // lowest prio & stale sequence + mPriorities.put(a.operation.getCreatorPackage(), packagePrio); + } + a.priorityClass = packagePrio; + + if (packagePrio.seq != mCurrentSeq) { + // first alarm we've seen in the current delivery generation from this package + packagePrio.priority = alarmPrio; + packagePrio.seq = mCurrentSeq; + } else { + // Multiple alarms from this package being delivered in this generation; + // bump the package's delivery class if it's warranted. + // TICK < WAKEUP < NORMAL + if (alarmPrio < packagePrio.priority) { + packagePrio.priority = alarmPrio; + } + } + } + } + // minimum recurrence period or alarm futurity for us to be able to fuzz it static final long MIN_FUZZABLE_INTERVAL = 10000; static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); @@ -1381,6 +1441,10 @@ class AlarmManagerService extends SystemService { } } + // This is a new alarm delivery set; bump the sequence number to indicate that + // all apps' alarm delivery classes should be recalculated. + mCurrentSeq++; + calculateDeliveryPriorities(triggerList); Collections.sort(triggerList, mAlarmDispatchComparator); if (localLOGV) { @@ -1423,6 +1487,7 @@ class AlarmManagerService extends SystemService { public long repeatInterval; public final AlarmManager.AlarmClockInfo alarmClock; public final int userId; + public PriorityClass priorityClass; public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, long _interval, PendingIntent _op, WorkSource _ws, @@ -1676,6 +1741,7 @@ class AlarmManagerService extends SystemService { rescheduleKernelAlarmsLocked(); updateNextAlarmClockLocked(); if (mPendingNonWakeupAlarms.size() > 0) { + calculateDeliveryPriorities(mPendingNonWakeupAlarms); triggerList.addAll(mPendingNonWakeupAlarms); Collections.sort(triggerList, mAlarmDispatchComparator); final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; @@ -1889,6 +1955,7 @@ class AlarmManagerService extends SystemService { if (pkgList != null && (pkgList.length > 0)) { for (String pkg : pkgList) { removeLocked(pkg); + mPriorities.remove(pkg); for (int i=mBroadcastStats.size()-1; i>=0; i--) { ArrayMap uidStats = mBroadcastStats.valueAt(i); if (uidStats.remove(pkg) != null) {