Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b62aa553 authored by Christopher Tate's avatar Christopher Tate Committed by Bart Sears
Browse files

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 81f9882b)
parent c82392df
Loading
Loading
Loading
Loading
+63 −23
Original line number Diff line number Diff line
@@ -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;