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

Commit 1590f1ee authored by Christopher Tate's avatar Christopher Tate
Browse files

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
parent 9f64867d
Loading
Loading
Loading
Loading
+73 −6
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.TimeZone;
@@ -141,6 +142,25 @@ class AlarmManagerService extends SystemService {
    private final SparseArray<AlarmManager.AlarmClockInfo> mHandlerSparseAlarmClockArray =
            new SparseArray<>();

    // Alarm delivery ordering bookkeeping
    static final int PRIO_TICK = 0;
    static final int PRIO_WAKEUP = 1;
    static final int PRIO_NORMAL = 2;

    class PriorityClass {
        int seq;
        int priority;

        PriorityClass() {
            seq = mCurrentSeq - 1;
            priority = PRIO_NORMAL;
        }
    }

    final HashMap<String, PriorityClass> mPriorities =
            new HashMap<String, PriorityClass>();
    int mCurrentSeq = 0;

    class WakeupEvent {
        public long when;
        public int uid;
@@ -356,22 +376,62 @@ class AlarmManagerService extends SystemService {
    final Comparator<Alarm> mAlarmDispatchComparator = new Comparator<Alarm>() {
        @Override
        public int compare(Alarm lhs, Alarm rhs) {
            if ((!lhs.operation.getCreatorPackage().equals(rhs.operation.getCreatorPackage()))
                    && lhs.wakeup != rhs.wakeup) {
                // We want to put wakeup alarms before non-wakeup alarms, since they are
                // the things that drive most activity in the alarm manager.  However,
                // alarms from the same package should always be ordered strictly by time.
                return lhs.wakeup ? -1 : 1;
            // priority class trumps everything.  TICK < WAKEUP < NORMAL
            if (lhs.priorityClass.priority < rhs.priorityClass.priority) {
                return -1;
            } else if (lhs.priorityClass.priority > rhs.priorityClass.priority) {
                return 1;
            }

            // within each class, sort by nominal delivery time
            if (lhs.whenElapsed < rhs.whenElapsed) {
                return -1;
            } else if (lhs.whenElapsed > rhs.whenElapsed) {
                return 1;
            }

            // same priority class + same target delivery time
            return 0;
        }
    };

    void calculateDeliveryPriorities(ArrayList<Alarm> alarms) {
        final int N = alarms.size();
        for (int i = 0; i < N; i++) {
            Alarm a = alarms.get(i);

            final int alarmPrio;
            if (Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
                alarmPrio = PRIO_TICK;
            } else if (a.wakeup) {
                alarmPrio = PRIO_WAKEUP;
            } else {
                alarmPrio = PRIO_NORMAL;
            }

            PriorityClass packagePrio = a.priorityClass;
            if (packagePrio == null) packagePrio = mPriorities.get(a.operation.getCreatorPackage());
            if (packagePrio == null) {
                packagePrio = a.priorityClass = new PriorityClass(); // lowest prio & stale sequence
                mPriorities.put(a.operation.getCreatorPackage(), packagePrio);
            }
            a.priorityClass = packagePrio;

            if (packagePrio.seq != mCurrentSeq) {
                // first alarm we've seen in the current delivery generation from this package
                packagePrio.priority = alarmPrio;
                packagePrio.seq = mCurrentSeq;
            } else {
                // Multiple alarms from this package being delivered in this generation;
                // bump the package's delivery class if it's warranted.
                // TICK < WAKEUP < NORMAL
                if (alarmPrio < packagePrio.priority) {
                    packagePrio.priority = alarmPrio;
                }
            }
        }
    }

    // minimum recurrence period or alarm futurity for us to be able to fuzz it
    static final long MIN_FUZZABLE_INTERVAL = 10000;
    static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
@@ -1381,6 +1441,10 @@ class AlarmManagerService extends SystemService {
            }
        }

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

        if (localLOGV) {
@@ -1423,6 +1487,7 @@ class AlarmManagerService extends SystemService {
        public long repeatInterval;
        public final AlarmManager.AlarmClockInfo alarmClock;
        public final int userId;
        public PriorityClass priorityClass;

        public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
                long _interval, PendingIntent _op, WorkSource _ws,
@@ -1676,6 +1741,7 @@ class AlarmManagerService extends SystemService {
                        rescheduleKernelAlarmsLocked();
                        updateNextAlarmClockLocked();
                        if (mPendingNonWakeupAlarms.size() > 0) {
                            calculateDeliveryPriorities(mPendingNonWakeupAlarms);
                            triggerList.addAll(mPendingNonWakeupAlarms);
                            Collections.sort(triggerList, mAlarmDispatchComparator);
                            final long thisDelayTime = nowELAPSED - mStartCurrentDelayTime;
@@ -1889,6 +1955,7 @@ class AlarmManagerService extends SystemService {
                if (pkgList != null && (pkgList.length > 0)) {
                    for (String pkg : pkgList) {
                        removeLocked(pkg);
                        mPriorities.remove(pkg);
                        for (int i=mBroadcastStats.size()-1; i>=0; i--) {
                            ArrayMap<String, BroadcastStats> uidStats = mBroadcastStats.valueAt(i);
                            if (uidStats.remove(pkg) != null) {