Loading apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +14 −3 Original line number Original line Diff line number Diff line Loading @@ -50,6 +50,9 @@ public final class TimeController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); || Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting static final long DELAY_COALESCE_TIME_MS = 30_000L; /** Deadline alarm tag for logging purposes */ /** Deadline alarm tag for logging purposes */ private final String DEADLINE_TAG = "*job.deadline*"; private final String DEADLINE_TAG = "*job.deadline*"; /** Delay alarm tag for logging purposes */ /** Delay alarm tag for logging purposes */ Loading @@ -57,6 +60,7 @@ public final class TimeController extends StateController { private long mNextJobExpiredElapsedMillis; private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private volatile long mLastFiredDelayExpiredElapsedMillis; private final boolean mChainedAttributionEnabled; private final boolean mChainedAttributionEnabled; Loading Loading @@ -273,7 +277,6 @@ public final class TimeController extends StateController { @VisibleForTesting @VisibleForTesting void checkExpiredDelaysAndResetAlarm() { void checkExpiredDelaysAndResetAlarm() { synchronized (mLock) { synchronized (mLock) { final long nowElapsedMillis = sElapsedRealtimeClock.millis(); long nextDelayTime = Long.MAX_VALUE; long nextDelayTime = Long.MAX_VALUE; int nextDelayUid = 0; int nextDelayUid = 0; String nextDelayPackageName = null; String nextDelayPackageName = null; Loading @@ -284,7 +287,7 @@ public final class TimeController extends StateController { if (!job.hasTimingDelayConstraint()) { if (!job.hasTimingDelayConstraint()) { continue; continue; } } if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) { if (canStopTrackingJobLocked(job)) { if (canStopTrackingJobLocked(job)) { it.remove(); it.remove(); } } Loading Loading @@ -356,7 +359,11 @@ public final class TimeController extends StateController { * This alarm <b>will not</b> wake up the phone. * This alarm <b>will not</b> wake up the phone. */ */ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { 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) { if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) { return; return; } } Loading Loading @@ -416,6 +423,7 @@ public final class TimeController extends StateController { if (DEBUG) { if (DEBUG) { Slog.d(TAG, "Delay-expired alarm fired"); Slog.d(TAG, "Delay-expired alarm fired"); } } mLastFiredDelayExpiredElapsedMillis = sElapsedRealtimeClock.millis(); checkExpiredDelaysAndResetAlarm(); checkExpiredDelaysAndResetAlarm(); } } }; }; Loading @@ -429,6 +437,9 @@ public final class TimeController extends StateController { pw.print("Next delay alarm in "); pw.print("Next delay alarm in "); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); pw.println(); pw.println(); pw.print("Last delay alarm fired @ "); TimeUtils.formatDuration(nowElapsed, mLastFiredDelayExpiredElapsedMillis, pw); pw.println(); pw.print("Next deadline alarm in "); pw.print("Next deadline alarm in "); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); pw.println(); pw.println(); Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +45 −0 Original line number Original line Diff line number Diff line Loading @@ -52,6 +52,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.MockitoSession; Loading Loading @@ -631,6 +632,50 @@ public class TimeControllerTest { .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any()); .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 @Test public void testEvaluateStateLocked_Delay() { public void testEvaluateStateLocked_Delay() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java +14 −3 Original line number Original line Diff line number Diff line Loading @@ -50,6 +50,9 @@ public final class TimeController extends StateController { private static final boolean DEBUG = JobSchedulerService.DEBUG private static final boolean DEBUG = JobSchedulerService.DEBUG || Log.isLoggable(TAG, Log.DEBUG); || Log.isLoggable(TAG, Log.DEBUG); @VisibleForTesting static final long DELAY_COALESCE_TIME_MS = 30_000L; /** Deadline alarm tag for logging purposes */ /** Deadline alarm tag for logging purposes */ private final String DEADLINE_TAG = "*job.deadline*"; private final String DEADLINE_TAG = "*job.deadline*"; /** Delay alarm tag for logging purposes */ /** Delay alarm tag for logging purposes */ Loading @@ -57,6 +60,7 @@ public final class TimeController extends StateController { private long mNextJobExpiredElapsedMillis; private long mNextJobExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private long mNextDelayExpiredElapsedMillis; private volatile long mLastFiredDelayExpiredElapsedMillis; private final boolean mChainedAttributionEnabled; private final boolean mChainedAttributionEnabled; Loading Loading @@ -273,7 +277,6 @@ public final class TimeController extends StateController { @VisibleForTesting @VisibleForTesting void checkExpiredDelaysAndResetAlarm() { void checkExpiredDelaysAndResetAlarm() { synchronized (mLock) { synchronized (mLock) { final long nowElapsedMillis = sElapsedRealtimeClock.millis(); long nextDelayTime = Long.MAX_VALUE; long nextDelayTime = Long.MAX_VALUE; int nextDelayUid = 0; int nextDelayUid = 0; String nextDelayPackageName = null; String nextDelayPackageName = null; Loading @@ -284,7 +287,7 @@ public final class TimeController extends StateController { if (!job.hasTimingDelayConstraint()) { if (!job.hasTimingDelayConstraint()) { continue; continue; } } if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) { if (evaluateTimingDelayConstraint(job, sElapsedRealtimeClock.millis())) { if (canStopTrackingJobLocked(job)) { if (canStopTrackingJobLocked(job)) { it.remove(); it.remove(); } } Loading Loading @@ -356,7 +359,11 @@ public final class TimeController extends StateController { * This alarm <b>will not</b> wake up the phone. * This alarm <b>will not</b> wake up the phone. */ */ private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) { 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) { if (mNextDelayExpiredElapsedMillis == alarmTimeElapsedMillis) { return; return; } } Loading Loading @@ -416,6 +423,7 @@ public final class TimeController extends StateController { if (DEBUG) { if (DEBUG) { Slog.d(TAG, "Delay-expired alarm fired"); Slog.d(TAG, "Delay-expired alarm fired"); } } mLastFiredDelayExpiredElapsedMillis = sElapsedRealtimeClock.millis(); checkExpiredDelaysAndResetAlarm(); checkExpiredDelaysAndResetAlarm(); } } }; }; Loading @@ -429,6 +437,9 @@ public final class TimeController extends StateController { pw.print("Next delay alarm in "); pw.print("Next delay alarm in "); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); TimeUtils.formatDuration(mNextDelayExpiredElapsedMillis, nowElapsed, pw); pw.println(); pw.println(); pw.print("Last delay alarm fired @ "); TimeUtils.formatDuration(nowElapsed, mLastFiredDelayExpiredElapsedMillis, pw); pw.println(); pw.print("Next deadline alarm in "); pw.print("Next deadline alarm in "); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); TimeUtils.formatDuration(mNextJobExpiredElapsedMillis, nowElapsed, pw); pw.println(); pw.println(); Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java +45 −0 Original line number Original line Diff line number Diff line Loading @@ -52,6 +52,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Before; import org.junit.Test; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.Mock; import org.mockito.MockitoSession; import org.mockito.MockitoSession; Loading Loading @@ -631,6 +632,50 @@ public class TimeControllerTest { .set(anyInt(), anyLong(), anyLong(), anyLong(), anyString(), any(), any(), any()); .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 @Test public void testEvaluateStateLocked_Delay() { public void testEvaluateStateLocked_Delay() { final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); final long now = JobSchedulerService.sElapsedRealtimeClock.millis(); Loading