Merge "Throttle how frequently delay alarms are set."

This commit is contained in:
TreeHugger Robot
2020-10-29 17:18:40 +00:00
committed by Android (Google) Code Review
2 changed files with 59 additions and 3 deletions

View File

@@ -50,6 +50,9 @@ public final class TimeController extends StateController {
private static final boolean DEBUG = JobSchedulerService.DEBUG
|| Log.isLoggable(TAG, Log.DEBUG);
@VisibleForTesting
static final long DELAY_COALESCE_TIME_MS = 30_000L;
/** Deadline alarm tag for logging purposes */
private final String DEADLINE_TAG = "*job.deadline*";
/** Delay alarm tag for logging purposes */
@@ -57,6 +60,7 @@ public final class TimeController extends StateController {
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
private volatile long mLastFiredDelayExpiredElapsedMillis;
private final boolean mChainedAttributionEnabled;
@@ -273,7 +277,6 @@ public final class TimeController extends StateController {
@VisibleForTesting
void checkExpiredDelaysAndResetAlarm() {
synchronized (mLock) {
final long nowElapsedMillis = sElapsedRealtimeClock.millis();
long nextDelayTime = Long.MAX_VALUE;
int nextDelayUid = 0;
String nextDelayPackageName = null;
@@ -284,7 +287,7 @@ public final class TimeController extends StateController {
if (!job.hasTimingDelayConstraint()) {
continue;
}
if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) {
if (canStopTrackingJobLocked(job)) {
it.remove();
}
@@ -356,7 +359,11 @@ public final class TimeController extends StateController {
* This alarm <b>will not</b> wake up the phone.
*/
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
// To avoid spamming AlarmManager in the case where many delay times are a few milliseconds
// apart, make sure the alarm is set no earlier than DELAY_COALESCE_TIME_MS since the last
// time a delay alarm went off and that the alarm is not scheduled for the past.
alarmTimeElapsedMillis = maybeAdjustAlarmTime(Math.max(alarmTimeElapsedMillis,
mLastFiredDelayExpiredElapsedMillis + DELAY_COALESCE_TIME_MS));
if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) {
return;
}
@@ -416,6 +423,7 @@ public final class TimeController extends StateController {
if (DEBUG) {
Slog.d(TAG, "Delay-expired alarm fired");
}
mLastFiredDelayExpiredElapsedMillis = sElapsedRealtimeClock.millis();
checkExpiredDelaysAndResetAlarm();
}
};
@@ -429,6 +437,9 @@ public final class TimeController extends StateController {
pw.print("Next delay alarm in ");
TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw);
pw.println();
pw.print("Last delay alarm fired @ ");
TimeUtils.formatDuration(nowElapsed, mLastFiredDelayExpiredElapsedMillis, pw);
pw.println();
pw.print("Next deadline alarm in ");
TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw);
pw.println();

View File

@@ -52,6 +52,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoSession;
@@ -631,6 +632,50 @@ public class TimeControllerTest {
.set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any());
}
@Test
public void testDelayAlarmSchedulingCoalescedIntervals() {
doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
JobStatus jobLatest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
createJob().setMinimumLatency(HOUR_IN_MILLIS));
JobStatus jobMiddle = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 2));
JobStatus jobEarliest = createJobStatus("testDelayAlarmSchedulingCoalescedIntervals",
createJob().setMinimumLatency(TimeController.DELAY_COALESCE_TIME_MS / 10));
ArgumentCaptor<AlarmManager.OnAlarmListener> listenerCaptor =
ArgumentCaptor.forClass(AlarmManager.OnAlarmListener.class);
InOrder inOrder = inOrder(mAlarmManager);
mTimeController.maybeStartTrackingJobLocked(jobEarliest, null);
mTimeController.maybeStartTrackingJobLocked(jobMiddle, null);
mTimeController.maybeStartTrackingJobLocked(jobLatest, null);
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10), anyLong(),
anyLong(), eq(TAG_DELAY),
listenerCaptor.capture(), any(), any());
final AlarmManager.OnAlarmListener delayListener = listenerCaptor.getValue();
advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS / 10);
delayListener.onAlarm();
// The next delay alarm time should be TimeController.DELAY_COALESCE_TIME_MS after the last
// time the delay alarm fired.
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(now + TimeController.DELAY_COALESCE_TIME_MS / 10
+ TimeController.DELAY_COALESCE_TIME_MS), anyLong(),
anyLong(), eq(TAG_DELAY), any(), any(), any());
advanceElapsedClock(TimeController.DELAY_COALESCE_TIME_MS);
delayListener.onAlarm();
// The last job is significantly after the coalesce time, so the 3rd scheduling shouldn't be
// affected by the first two jobs' alarms.
inOrder.verify(mAlarmManager, times(1))
.set(anyInt(), eq(now + HOUR_IN_MILLIS), anyLong(),
anyLong(), eq(TAG_DELAY), any(), any(), any());
}
@Test
public void testEvaluateStateLocked_Delay() {
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();