Skip to content

Commit 1590f1e

Browse files
author
Christopher Tate
committed
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
1 parent 9f64867 commit 1590f1e

1 file changed

Lines changed: 73 additions & 6 deletions

File tree

services/core/java/com/android/server/AlarmManagerService.java

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import java.util.Collections;
5959
import java.util.Comparator;
6060
import java.util.Date;
61+
import java.util.HashMap;
6162
import java.util.LinkedList;
6263
import java.util.Locale;
6364
import java.util.TimeZone;
@@ -141,6 +142,25 @@ class AlarmManagerService extends SystemService {
141142
private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
142143
new SparseArray<>();
143144

145+
// Alarm delivery ordering bookkeeping
146+
static final int PRIO_TICK = 0;
147+
static final int PRIO_WAKEUP = 1;
148+
static final int PRIO_NORMAL = 2;
149+
150+
class PriorityClass {
151+
int seq;
152+
int priority;
153+
154+
PriorityClass() {
155+
seq = mCurrentSeq - 1;
156+
priority = PRIO_NORMAL;
157+
}
158+
}
159+
160+
final HashMap<String, PriorityClass> mPriorities =
161+
new HashMap<String, PriorityClass>();
162+
int mCurrentSeq = 0;
163+
144164
class WakeupEvent {
145165
public long when;
146166
public int uid;
@@ -356,22 +376,62 @@ public int compare(Batch b1, Batch b2) {
356376
final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
357377
@Override
358378
public int compare(Alarm lhs, Alarm rhs) {
359-
if ((!lhs.operation.getCreatorPackage().equals(rhs.operation.getCreatorPackage()))
360-
&& lhs.wakeup != rhs.wakeup) {
361-
// We want to put wakeup alarms before non-wakeup alarms, since they are
362-
// the things that drive most activity in the alarm manager. However,
363-
// alarms from the same package should always be ordered strictly by time.
364-
return lhs.wakeup ? -1 : 1;
379+
// priority class trumps everything. TICK < WAKEUP < NORMAL
380+
if (lhs.priorityClass.priority < rhs.priorityClass.priority) {
381+
return -1;
382+
} else if (lhs.priorityClass.priority > rhs.priorityClass.priority) {
383+
return 1;
365384
}
385+
386+
// within each class, sort by nominal delivery time
366387
if (lhs.whenElapsed < rhs.whenElapsed) {
367388
return -1;
368389
} else if (lhs.whenElapsed > rhs.whenElapsed) {
369390
return 1;
370391
}
392+
393+
// same priority class + same target delivery time
371394
return 0;
372395
}
373396
};
374397

398+
void calculateDeliveryPriorities(ArrayList<Alarm> alarms) {
399+
final int N = alarms.size();
400+
for (int i = 0; i < N; i++) {
401+
Alarm a = alarms.get(i);
402+
403+
final int alarmPrio;
404+
if (Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
405+
alarmPrio = PRIO_TICK;
406+
} else if (a.wakeup) {
407+
alarmPrio = PRIO_WAKEUP;
408+
} else {
409+
alarmPrio = PRIO_NORMAL;
410+
}
411+
412+
PriorityClass packagePrio = a.priorityClass;
413+
if (packagePrio == null) packagePrio = mPriorities.get(a.operation.getCreatorPackage());
414+
if (packagePrio == null) {
415+
packagePrio = a.priorityClass = new PriorityClass(); // lowest prio & stale sequence
416+
mPriorities.put(a.operation.getCreatorPackage(), packagePrio);
417+
}
418+
a.priorityClass = packagePrio;
419+
420+
if (packagePrio.seq != mCurrentSeq) {
421+
// first alarm we've seen in the current delivery generation from this package
422+
packagePrio.priority = alarmPrio;
423+
packagePrio.seq = mCurrentSeq;
424+
} else {
425+
// Multiple alarms from this package being delivered in this generation;
426+
// bump the package's delivery class if it's warranted.
427+
// TICK < WAKEUP < NORMAL
428+
if (alarmPrio < packagePrio.priority) {
429+
packagePrio.priority = alarmPrio;
430+
}
431+
}
432+
}
433+
}
434+
375435
// minimum recurrence period or alarm futurity for us to be able to fuzz it
376436
static final long MIN_FUZZABLE_INTERVAL = 10000;
377437
static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
@@ -1381,6 +1441,10 @@ boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
13811441
}
13821442
}
13831443

1444+
// This is a new alarm delivery set; bump the sequence number to indicate that
1445+
// all apps' alarm delivery classes should be recalculated.
1446+
mCurrentSeq++;
1447+
calculateDeliveryPriorities(triggerList);
13841448
Collections.sort(triggerList, mAlarmDispatchComparator);
13851449

13861450
if (localLOGV) {
@@ -1423,6 +1487,7 @@ private static class Alarm {
14231487
public long repeatInterval;
14241488
public final AlarmManager.AlarmClockInfo alarmClock;
14251489
public final int userId;
1490+
public PriorityClass priorityClass;
14261491

14271492
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
14281493
long _interval, PendingIntent _op, WorkSource _ws,
@@ -1676,6 +1741,7 @@ public void run()
16761741
rescheduleKernelAlarmsLocked();
16771742
updateNextAlarmClockLocked();
16781743
if (mPendingNonWakeupAlarms.size() > 0) {
1744+
calculateDeliveryPriorities(mPendingNonWakeupAlarms);
16791745
triggerList.addAll(mPendingNonWakeupAlarms);
16801746
Collections.sort(triggerList, mAlarmDispatchComparator);
16811747
final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
@@ -1889,6 +1955,7 @@ public void onReceive(Context context, Intent intent) {
18891955
if (pkgList != null && (pkgList.length > 0)) {
18901956
for (String pkg : pkgList) {
18911957
removeLocked(pkg);
1958+
mPriorities.remove(pkg);
18921959
for (int i=mBroadcastStats.size()-1; i>=0; i--) {
18931960
ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
18941961
if (uidStats.remove(pkg) != null) {

0 commit comments

Comments
 (0)