|
58 | 58 | import java.util.Collections; |
59 | 59 | import java.util.Comparator; |
60 | 60 | import java.util.Date; |
| 61 | +import java.util.HashMap; |
61 | 62 | import java.util.LinkedList; |
62 | 63 | import java.util.Locale; |
63 | 64 | import java.util.TimeZone; |
@@ -141,6 +142,25 @@ class AlarmManagerService extends SystemService { |
141 | 142 | private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray = |
142 | 143 | new SparseArray<>(); |
143 | 144 |
|
| 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 | + |
144 | 164 | class WakeupEvent { |
145 | 165 | public long when; |
146 | 166 | public int uid; |
@@ -356,22 +376,62 @@ public int compare(Batch b1, Batch b2) { |
356 | 376 | final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() { |
357 | 377 | @Override |
358 | 378 | 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; |
365 | 384 | } |
| 385 | + |
| 386 | + // within each class, sort by nominal delivery time |
366 | 387 | if (lhs.whenElapsed < rhs.whenElapsed) { |
367 | 388 | return -1; |
368 | 389 | } else if (lhs.whenElapsed > rhs.whenElapsed) { |
369 | 390 | return 1; |
370 | 391 | } |
| 392 | + |
| 393 | + // same priority class + same target delivery time |
371 | 394 | return 0; |
372 | 395 | } |
373 | 396 | }; |
374 | 397 |
|
| 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 | + |
375 | 435 | // minimum recurrence period or alarm futurity for us to be able to fuzz it |
376 | 436 | static final long MIN_FUZZABLE_INTERVAL = 10000; |
377 | 437 | static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); |
@@ -1381,6 +1441,10 @@ boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED, |
1381 | 1441 | } |
1382 | 1442 | } |
1383 | 1443 |
|
| 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); |
1384 | 1448 | Collections.sort(triggerList, mAlarmDispatchComparator); |
1385 | 1449 |
|
1386 | 1450 | if (localLOGV) { |
@@ -1423,6 +1487,7 @@ private static class Alarm { |
1423 | 1487 | public long repeatInterval; |
1424 | 1488 | public final AlarmManager.AlarmClockInfo alarmClock; |
1425 | 1489 | public final int userId; |
| 1490 | + public PriorityClass priorityClass; |
1426 | 1491 |
|
1427 | 1492 | public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, |
1428 | 1493 | long _interval, PendingIntent _op, WorkSource _ws, |
@@ -1676,6 +1741,7 @@ public void run() |
1676 | 1741 | rescheduleKernelAlarmsLocked(); |
1677 | 1742 | updateNextAlarmClockLocked(); |
1678 | 1743 | if (mPendingNonWakeupAlarms.size() > 0) { |
| 1744 | + calculateDeliveryPriorities(mPendingNonWakeupAlarms); |
1679 | 1745 | triggerList.addAll(mPendingNonWakeupAlarms); |
1680 | 1746 | Collections.sort(triggerList, mAlarmDispatchComparator); |
1681 | 1747 | final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime; |
@@ -1889,6 +1955,7 @@ public void onReceive(Context context, Intent intent) { |
1889 | 1955 | if (pkgList != null && (pkgList.length > 0)) { |
1890 | 1956 | for (String pkg : pkgList) { |
1891 | 1957 | removeLocked(pkg); |
| 1958 | + mPriorities.remove(pkg); |
1892 | 1959 | for (int i=mBroadcastStats.size()-1; i>=0; i--) { |
1893 | 1960 | ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i); |
1894 | 1961 | if (uidStats.remove(pkg) != null) { |
|
0 commit comments