From 74b85211fa12e3e0585fb538f313c234c3e0c27b Mon Sep 17 00:00:00 2001 From: Suprabh Shukla Date: Mon, 3 May 2021 19:46:34 -0700 Subject: [PATCH] Hold wakeup alarms for as long as possible If the screen is off, but the CPU is awake and epoll_wait returns for a non-wakeup alarm, sending any wakeup alarm within its eligibility window would cause all the held non-wakeup alarms to also fire, which could: 1. Elongate the length of the time the CPU is awake. 2. Cause apps to wake up and perform work. This change defers sending any wakeup alarm as long as it is possible to defer all such eligible alarms to later. Also, fixed a bug in sorting after adding multiple alarms to the store. Test: atest CtsAlarmManagerTestCases atest FrameworksMockingServicesTests:com.android.server.alarm Bug: 161497385 Change-Id: I01f98255091baddf4e7529f88733776e27d090d7 --- .../android/server/alarm/LazyAlarmStore.java | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java index 2e12e2f34ea07..0073335a13320 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/LazyAlarmStore.java @@ -40,6 +40,7 @@ import java.util.function.Predicate; public class LazyAlarmStore implements AlarmStore { @VisibleForTesting static final String TAG = LazyAlarmStore.class.getSimpleName(); + private static final long ALARM_DEADLINE_SLOP = 500; private final ArrayList mAlarms = new ArrayList<>(); private Runnable mOnAlarmClockRemoved; @@ -75,7 +76,7 @@ public class LazyAlarmStore implements AlarmStore { return; } mAlarms.addAll(alarms); - Collections.sort(alarms, sDecreasingTimeOrder); + Collections.sort(mAlarms, sDecreasingTimeOrder); } @Override @@ -163,25 +164,47 @@ public class LazyAlarmStore implements AlarmStore { @Override public ArrayList removePendingAlarms(long nowElapsed) { final ArrayList pending = new ArrayList<>(); - final ArrayList standAlones = new ArrayList<>(); + + // Only send wake-up alarms if this is the absolutely latest time we can evaluate + // for at least one wakeup alarm. This prevents sending other non-wakeup alarms when the + // screen is off but the CPU is awake for some reason. + boolean sendWakeups = false; + + // If any alarm with FLAG_STANDALONE is present, we cannot send any alarms without that flag + // in the present batch. + boolean standalonesOnly = false; for (int i = mAlarms.size() - 1; i >= 0; i--) { final Alarm alarm = mAlarms.get(i); if (alarm.getWhenElapsed() > nowElapsed) { break; } + mAlarms.remove(i); pending.add(alarm); + if (alarm.wakeup && alarm.getMaxWhenElapsed() <= nowElapsed + ALARM_DEADLINE_SLOP) { + // Using some slop as it is better to send the wakeup alarm now, rather than + // waking up again a short time later, just to send it. + sendWakeups = true; + } if ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) { - standAlones.add(alarm); + standalonesOnly = true; } } - if (!standAlones.isEmpty()) { - // If there are deliverable standalone alarms, others must not go out yet. - mAlarms.removeAll(standAlones); - return standAlones; + final ArrayList toSend = new ArrayList<>(); + for (int i = pending.size() - 1; i >= 0; i--) { + final Alarm pendingAlarm = pending.get(i); + if (!sendWakeups && pendingAlarm.wakeup) { + continue; + } + if (standalonesOnly && (pendingAlarm.flags & AlarmManager.FLAG_STANDALONE) == 0) { + continue; + } + pending.remove(i); + toSend.add(pendingAlarm); } - mAlarms.removeAll(pending); - return pending; + // Perhaps some alarms could not be sent right now. Adding them back for later. + addAll(pending); + return toSend; } @Override