diff options
author | Christopher Tate <ctate@google.com> | 2014-12-04 18:27:16 -0800 |
---|---|---|
committer | The Android Automerger <android-build@google.com> | 2014-12-11 18:03:20 -0800 |
commit | a6e7e6bf5923f427785d6946ac050ba4e4052dc5 (patch) | |
tree | ed21fc9037c93c289fdbd4c9227201c843c7cb46 | |
parent | 5a995f968ca0e5c9701e36f0240b4708ea69c78d (diff) | |
download | base-a6e7e6bf5923f427785d6946ac050ba4e4052dc5.tar.gz |
Tune delivery and batching of alarms
cherry-pick from lmp-mr1-dev
* Inexact alarms no longer coalesce with exact alarms. The motivation here
is that exact alarms are far more likely to be wall-clock aligned, and in
general pulling all alarms toward wall-clock alignment is a bad idea.
* Wakeup times are now fuzzed within the target batch's allowed window
rather than being hard pinned at the start of the window.
Bug 18631821
Change-Id: Iefaf34eee3f2a6546abefc27e177ee2fdcff935f
(cherry picked from commit 81f9882b5aadd6a2289c9f521a06a7af5f35ebf0)
-rw-r--r-- | services/core/java/com/android/server/AlarmManagerService.java | 86 |
1 files changed, 63 insertions, 23 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index 9d6ccfb03882..ed8bc51f0de9 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -61,6 +61,7 @@ import java.util.Date; import java.util.HashMap; import java.util.LinkedList; import java.util.Locale; +import java.util.Random; import java.util.TimeZone; import static android.app.AlarmManager.RTC_WAKEUP; @@ -108,8 +109,12 @@ class AlarmManagerService extends SystemService { final Object mLock = new Object(); long mNativeData; - private long mNextWakeup; + + private final Random mFuzzer = new Random(); + private long mNextWakeupBatchStart; // nominal start of next wakeup's delivery window + private long mNextWakeup; // actual scheduled next wakeup within that window private long mNextNonWakeup; + int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; boolean mLastWakeLockUnimportantForLogging; @@ -361,14 +366,27 @@ class AlarmManagerService extends SystemService { static class BatchTimeOrder implements Comparator<Batch> { public int compare(Batch b1, Batch b2) { - long when1 = b1.start; - long when2 = b2.start; - if (when1 - when2 > 0) { + final long start1 = b1.start; + final long start2 = b2.start; + if (start1 > start2) { return 1; } - if (when1 - when2 < 0) { + if (start1 < start2) { return -1; } + + // Identical trigger times. As a secondary ordering, require that + // the batch with the shorter allowable delivery window sorts first. + final long interval1 = b1.end - b1.start; + final long interval2 = b2.end - b2.start; + if (interval1 > interval2) { + return 1; + } + if (interval2 < interval1) { + return -1; + } + + // equal start + delivery window => they're identical return 0; } } @@ -591,7 +609,7 @@ class AlarmManagerService extends SystemService { @Override public void onStart() { mNativeData = init(); - mNextWakeup = mNextNonWakeup = 0; + mNextWakeup = mNextWakeupBatchStart = mNextNonWakeup = 0; // We have to set current TimeZone info to kernel // because kernel doesn't keep this after reboot @@ -786,8 +804,9 @@ class AlarmManagerService extends SystemService { "AlarmManager.set"); } + // Exact alarms are standalone; inexact get batched together setImpl(type, triggerAtTime, windowLength, interval, operation, - false, workSource, alarmClock); + windowLength == AlarmManager.WINDOW_EXACT, workSource, alarmClock); } @Override @@ -858,7 +877,7 @@ class AlarmManagerService extends SystemService { pw.print("nowRTC="); pw.print(nowRTC); pw.print("="); pw.print(sdf.format(new Date(nowRTC))); - pw.print(" nowELAPSED="); TimeUtils.formatDuration(nowELAPSED, pw); + pw.print(" nowELAPSED="); pw.print(nowELAPSED); pw.println(); if (!mInteractive) { pw.print("Time since non-interactive: "); @@ -1064,17 +1083,6 @@ class AlarmManagerService extends SystemService { return true; } - private Batch findFirstWakeupBatchLocked() { - final int N = mAlarmBatches.size(); - for (int i = 0; i < N; i++) { - Batch b = mAlarmBatches.get(i); - if (b.hasWakeups()) { - return b; - } - } - return null; - } - private AlarmManager.AlarmClockInfo getNextAlarmClockImpl(int userId) { synchronized (mLock) { return mNextAlarmClockForUser.get(userId); @@ -1208,16 +1216,48 @@ class AlarmManagerService extends SystemService { // prior to that which contains no wakeups, we schedule that as well. long nextNonWakeup = 0; if (mAlarmBatches.size() > 0) { - final Batch firstWakeup = findFirstWakeupBatchLocked(); + // Find the first wakeup alarm and note the following batch as well. We'll be + // choosing a fuzzed delivery time within the first's allowable interval but + // ensuring that it does not encroach on the second's start time, to minimize + // alarm reordering. + Batch firstWakeup = null, nextAfterWakeup = null; + final int N = mAlarmBatches.size(); + for (int i = 0; i < N; i++) { + Batch b = mAlarmBatches.get(i); + if (b.hasWakeups()) { + firstWakeup = b; + if (i < N-1) { + nextAfterWakeup = mAlarmBatches.get(i+1); + } + break; + } + } + + // There's a subtlety here: we depend on the invariant that if two batches + // exist with the same start time, the one with the shorter delivery window + // is sorted before the other. This guarantees us that we need only look + // at the first [relevant] batch in the queue in order to schedule an alarm + // appropriately. final Batch firstBatch = mAlarmBatches.get(0); - if (firstWakeup != null && mNextWakeup != firstWakeup.start) { - mNextWakeup = firstWakeup.start; - setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); + if (firstWakeup != null && mNextWakeupBatchStart != firstWakeup.start) { + mNextWakeupBatchStart = mNextWakeup = firstWakeup.start; + final long windowEnd = (nextAfterWakeup == null) + ? firstWakeup.end + : Math.min(firstWakeup.end, nextAfterWakeup.start); + final long interval = windowEnd - firstWakeup.start; + // if the interval is over maxint we're into crazy land anyway, but + // just in case we check and don't fuzz if the conversion to int for + // random-number purposes would blow up + if (interval > 0 && interval < Integer.MAX_VALUE) { + mNextWakeup += mFuzzer.nextInt((int) interval); + } + setLocked(ELAPSED_REALTIME_WAKEUP, mNextWakeup); } if (firstBatch != firstWakeup) { nextNonWakeup = firstBatch.start; } } + if (mPendingNonWakeupAlarms.size() > 0) { if (nextNonWakeup == 0 || mNextNonWakeupDeliveryTime < nextNonWakeup) { nextNonWakeup = mNextNonWakeupDeliveryTime; |