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

Commit b8849c1e authored by Christopher Tate's avatar Christopher Tate
Browse files

Align inexact alarms in both time bases; don't pull to wall time

The previous implementation failed to properly distinguish between
trigger times in the RTC vs the ELAPSED time bases.  The pernicious
result of this was that sometimes it would decide to align RTC
alarms based on, say, 0 rather than on the real current time.
This would pull the recurrence into wall-clock alignment, with
serious side effects: in particular, periodic tasks that would hit
external network resources would, because *all* devices would be
pulled into wall-clock alignment, wind up inducing heavy QPS
spikes on the backends.

The new implementation works completely differently.  The basic
goal is the same: try to align inexact alarms to "the same" time,
avoiding extra wakeups / radio spinups / etc.  The way this is done
is to divide the timeline into 15-minute quanta, and drift the start
time of every inexact alarm onto one of these 15-minute quantum
boundaries.  The skew between the RTC and ELAPSED time bases is
taken into effect; RTC alarms are no longer pulled into wall clock
alignment.

Fixes bug 3388961

Change-Id: I2a0460e1f5d0e4036f3402f332b642b557b2fc20
parent 5ed9a805
Loading
Loading
Loading
Loading
+30 −56
Original line number Diff line number Diff line
@@ -64,6 +64,9 @@ class AlarmManagerService extends IAlarmManager.Stub {
    private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
    private static final int TIME_CHANGED_MASK = 1 << 16;

    // Alignment quantum for inexact repeating alarms
    private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;

    private static final String TAG = "AlarmManager";
    private static final String ClockReceiver_TAG = "ClockReceiver";
    private static final boolean localLOGV = false;
@@ -83,17 +86,6 @@ class AlarmManagerService extends IAlarmManager.Stub {
    private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
    private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
    
    // slots corresponding with the inexact-repeat interval buckets,
    // ordered from shortest to longest
    private static final long sInexactSlotIntervals[] = {
        AlarmManager.INTERVAL_FIFTEEN_MINUTES,
        AlarmManager.INTERVAL_HALF_HOUR,
        AlarmManager.INTERVAL_HOUR,
        AlarmManager.INTERVAL_HALF_DAY,
        AlarmManager.INTERVAL_DAY
    };
    private long mInexactDeliveryTimes[] = { 0, 0, 0, 0, 0};
    
    private int mDescriptor;
    private int mBroadcastRefCount = 0;
    private PowerManager.WakeLock mWakeLock;
@@ -199,58 +191,40 @@ class AlarmManagerService extends IAlarmManager.Stub {
            return;
        }

        // find the slot in the delivery-times array that we will use
        int intervalSlot;
        for (intervalSlot = 0; intervalSlot < sInexactSlotIntervals.length; intervalSlot++) {
            if (sInexactSlotIntervals[intervalSlot] == interval) {
                break;
            }
        if (interval <= 0) {
            Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
                    + " is invalid");
            return;
        }

        // Non-bucket intervals just fall back to the less-efficient
        // unbucketed recurring alarm implementation
        if (intervalSlot >= sInexactSlotIntervals.length) {
        // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
        if (interval % QUANTUM != 0) {
            if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
            setRepeating(type, triggerAtTime, interval, operation);
            return;
        }

        // Align bucketed alarm deliveries by trying to match
        // the shortest-interval bucket already scheduled
        long bucketTime = 0;
        for (int slot = 0; slot < mInexactDeliveryTimes.length; slot++) {
            if (mInexactDeliveryTimes[slot] > 0) {
                bucketTime = mInexactDeliveryTimes[slot];
                break;
            }
        }
        // Translate times into the ELAPSED timebase for alignment purposes so that
        // alignment never tries to match against wall clock times.
        final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
        final long skew = (isRtc)
                ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
                : 0;

        if (bucketTime == 0) {
            // If nothing is scheduled yet, just start at the requested time
            bucketTime = triggerAtTime;
        // Slip forward to the next ELAPSED-timebase quantum after the stated time.  If
        // we're *at* a quantum point, leave it alone.
        final long adjustedTriggerTime;
        long offset = (triggerAtTime - skew) % QUANTUM;
        if (offset != 0) {
            adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
        } else {
            // Align the new alarm with the existing bucketed sequence.  To achieve
            // alignment, we slide the start time around by min{interval, slot interval}
            long adjustment = (interval <= sInexactSlotIntervals[intervalSlot])
                    ? interval : sInexactSlotIntervals[intervalSlot];

            // The bucket may have started in the past; adjust
            while (bucketTime < triggerAtTime) {
                bucketTime += adjustment;
            }

            // Or the bucket may be set to start more than an interval beyond
            // our requested trigger time; pull it back to meet our needs
            while (bucketTime > triggerAtTime + adjustment) {
                bucketTime -= adjustment;
            }
            adjustedTriggerTime = triggerAtTime;
        }

        // Remember where this bucket started (reducing the amount of later 
        // fixup required) and set the alarm with the new, bucketed start time.
        if (localLOGV) Slog.v(TAG, "setInexactRepeating: interval=" + interval
                + " bucketTime=" + bucketTime);
        mInexactDeliveryTimes[intervalSlot] = bucketTime;
        setRepeating(type, bucketTime, interval, operation);
        // Set the alarm based on the quantum-aligned start time
        if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
                + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
        setRepeating(type, adjustedTriggerTime, interval, operation);
    }

    public void setTime(long millis) {