Loading apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +58 −23 Original line number Diff line number Diff line Loading @@ -241,6 +241,8 @@ public final class FlexibilityController extends StateController { private static final long MAX_TIME_WINDOW_MS = 24 * HOUR_IN_MILLIS; private final JobScoreBucket[] mScoreBuckets = new JobScoreBucket[NUM_SCORE_BUCKETS]; private int mScoreBucketIndex = 0; private long mCachedScoreExpirationTimeElapsed; private int mCachedScore; public void addScore(int add, long nowElapsed) { JobScoreBucket bucket = mScoreBuckets[mScoreBucketIndex]; Loading @@ -248,10 +250,17 @@ public final class FlexibilityController extends StateController { bucket = new JobScoreBucket(); bucket.startTimeElapsed = nowElapsed; mScoreBuckets[mScoreBucketIndex] = bucket; // Brand new bucket, there's nothing to remove from the score, // so just update the expiration time if needed. mCachedScoreExpirationTimeElapsed = Math.min(mCachedScoreExpirationTimeElapsed, nowElapsed + MAX_TIME_WINDOW_MS); } else if (bucket.startTimeElapsed < nowElapsed - MAX_TIME_WINDOW_MS) { // The bucket is too old. bucket.reset(); bucket.startTimeElapsed = nowElapsed; // Force a recalculation of the cached score instead of just updating the cached // value and time in case there are multiple stale buckets. mCachedScoreExpirationTimeElapsed = nowElapsed; } else if (bucket.startTimeElapsed < nowElapsed - MAX_TIME_WINDOW_MS / NUM_SCORE_BUCKETS) { // The current bucket's duration has completed. Move on to the next bucket. Loading @@ -261,16 +270,26 @@ public final class FlexibilityController extends StateController { } bucket.score += add; mCachedScore += add; } public int getScore(long nowElapsed) { if (nowElapsed < mCachedScoreExpirationTimeElapsed) { return mCachedScore; } int score = 0; final long earliestElapsed = nowElapsed - MAX_TIME_WINDOW_MS; long earliestValidBucketTimeElapsed = Long.MAX_VALUE; for (JobScoreBucket bucket : mScoreBuckets) { if (bucket != null && bucket.startTimeElapsed >= earliestElapsed) { score += bucket.score; if (earliestValidBucketTimeElapsed > bucket.startTimeElapsed) { earliestValidBucketTimeElapsed = bucket.startTimeElapsed; } } } mCachedScore = score; mCachedScoreExpirationTimeElapsed = earliestValidBucketTimeElapsed + MAX_TIME_WINDOW_MS; return score; } Loading Loading @@ -378,10 +397,16 @@ public final class FlexibilityController extends StateController { @Override public void prepareForExecutionLocked(JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) { // Don't include jobs for the TOP app in the score calculation. return; } // Use the job's requested priority to determine its score since that is what the developer // selected and it will be stable across job runs. final int score = mFallbackFlexibilityDeadlineScores .get(jobStatus.getJob().getPriority(), jobStatus.getJob().getPriority() / 100); final int priority = jobStatus.getJob().getPriority(); final int score = mFallbackFlexibilityDeadlineScores.get(priority, FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_SCORES .get(priority, priority / 100)); JobScoreTracker jobScoreTracker = mJobScoreTrackers.get(jobStatus.getSourceUid(), jobStatus.getSourcePackageName()); if (jobScoreTracker == null) { Loading @@ -394,6 +419,10 @@ public final class FlexibilityController extends StateController { @Override public void unprepareFromExecutionLocked(JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) { // Jobs for the TOP app are excluded from the score calculation. return; } // The job didn't actually start. Undo the score increase. JobScoreTracker jobScoreTracker = mJobScoreTrackers.get(jobStatus.getSourceUid(), jobStatus.getSourcePackageName()); Loading @@ -401,8 +430,10 @@ public final class FlexibilityController extends StateController { Slog.e(TAG, "Unprepared a job that didn't result in a score change"); return; } final int score = mFallbackFlexibilityDeadlineScores .get(jobStatus.getJob().getPriority(), jobStatus.getJob().getPriority() / 100); final int priority = jobStatus.getJob().getPriority(); final int score = mFallbackFlexibilityDeadlineScores.get(priority, FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_SCORES .get(priority, priority / 100)); jobScoreTracker.addScore(-score, sElapsedRealtimeClock.millis()); } Loading Loading @@ -649,7 +680,7 @@ public final class FlexibilityController extends StateController { (long) Math.scalb(mRescheduledJobDeadline, js.getNumPreviousAttempts() - 2), mMaxRescheduledDeadline); } if (js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME) { // Intentionally use the effective priority here. If a job's priority was effectively // lowered, it will be less likely to run quickly given other policies in JobScheduler. // Thus, there's no need to further delay the job based on flex policy. Loading @@ -657,13 +688,16 @@ public final class FlexibilityController extends StateController { final int jobScore = getScoreLocked(js.getSourceUid(), js.getSourcePackageName(), nowElapsed); // Set an upper limit on the fallback deadline so that the delay doesn't become extreme. final long fallbackDeadlineMs = Math.min(3 * mFallbackFlexibilityDeadlineMs, final long fallbackDurationMs = Math.min(3 * mFallbackFlexibilityDeadlineMs, mFallbackFlexibilityDeadlines.get(jobPriority, mFallbackFlexibilityDeadlineMs) + mFallbackFlexibilityAdditionalScoreTimeFactors .get(jobPriority, MINUTE_IN_MILLIS) * jobScore); return earliest + fallbackDeadlineMs; final long fallbackDeadlineMs = earliest + fallbackDurationMs; if (js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME) { return fallbackDeadlineMs; } return js.getLatestRunTimeElapsed(); return Math.max(fallbackDeadlineMs, js.getLatestRunTimeElapsed()); } @VisibleForTesting Loading Loading @@ -976,7 +1010,8 @@ public final class FlexibilityController extends StateController { // Something has gone horribly wrong. This has only occurred on incorrectly // configured tests, but add a check here for safety. Slog.wtf(TAG, "Got invalid latest when scheduling alarm." + " Prefetch=" + js.getJob().isPrefetch()); + " prefetch=" + js.getJob().isPrefetch() + " periodic=" + js.getJob().isPeriodic()); // Since things have gone wrong, the safest and most reliable thing to do is // stop applying flex policy to the job. mFlexibilityTracker.setNumDroppedFlexibleConstraints(js, Loading @@ -991,7 +1026,7 @@ public final class FlexibilityController extends StateController { if (DEBUG) { Slog.d(TAG, "scheduleDropNumConstraintsAlarm: " + js.getSourcePackageName() + " " + js.getSourceUserId() + js.toShortString() + " numApplied: " + js.getNumAppliedFlexibleConstraints() + " numRequired: " + js.getNumRequiredFlexibleConstraints() + " numSatisfied: " + Integer.bitCount( Loading Loading @@ -1199,11 +1234,11 @@ public final class FlexibilityController extends StateController { DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_MAX, 0); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_HIGH, 4 * MINUTE_IN_MILLIS); .put(PRIORITY_HIGH, 3 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_DEFAULT, 3 * MINUTE_IN_MILLIS); .put(PRIORITY_DEFAULT, 2 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_LOW, 2 * MINUTE_IN_MILLIS); .put(PRIORITY_LOW, 1 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_MIN, 1 * MINUTE_IN_MILLIS); DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS Loading @@ -1220,7 +1255,7 @@ public final class FlexibilityController extends StateController { private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS; private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS; private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS; private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = DAY_IN_MILLIS; @VisibleForTesting static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS; Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +0 −9 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.job.controllers; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; Loading Loading @@ -430,9 +428,6 @@ public final class JobStatus { */ public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2; /** Minimum difference between start and end time to have flexible constraint */ @VisibleForTesting static final long MIN_WINDOW_FOR_FLEXIBILITY_MS = HOUR_IN_MILLIS; /** * Versatile, persistable flags for a job that's updated within the system server, * as opposed to {@link JobInfo#flags} that's set by callers. Loading Loading @@ -708,14 +703,10 @@ public final class JobStatus { final boolean lacksSomeFlexibleConstraints = ((~requiredConstraints) & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS) != 0 || mCanApplyTransportAffinities; final boolean satisfiesMinWindowException = (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis) >= MIN_WINDOW_FOR_FLEXIBILITY_MS; // The first time a job is rescheduled it will not be subject to flexible constraints. // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline. if (!isRequestedExpeditedJob() && !job.isUserInitiated() && satisfiesMinWindowException && (numFailures + numSystemStops) != 1 && lacksSomeFlexibleConstraints) { requiredConstraints |= CONSTRAINT_FLEXIBLE; Loading services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +61 −48 Original line number Diff line number Diff line Loading @@ -49,7 +49,6 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVI import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS; import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME; import static org.junit.Assert.assertArrayEquals; Loading Loading @@ -410,10 +409,12 @@ public class FlexibilityControllerTest { @Test public void testOnConstantsUpdated_PercentsToDropConstraints() { final long fallbackDuration = 12 * HOUR_IN_MILLIS; JobInfo.Builder jb = createJob(0) .setOverrideDeadline(MIN_WINDOW_FOR_FLEXIBILITY_MS); .setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, // Even though the override deadline is 1 hour, the fallback duration is still used. assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, "500=1|2|3|4" Loading Loading @@ -441,13 +442,13 @@ public class FlexibilityControllerTest { mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS .get(JobInfo.PRIORITY_MIN), new int[]{54, 55, 56, 57}); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10, assertEquals(FROZEN_TIME + fallbackDuration / 10, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); js.setNumDroppedFlexibleConstraints(1); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 2, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 2, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); js.setNumDroppedFlexibleConstraints(2); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 3, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 3, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); } Loading Loading @@ -504,37 +505,38 @@ public class FlexibilityControllerTest { @Test public void testGetNextConstraintDropTimeElapsedLocked() { final long fallbackDuration = 50 * HOUR_IN_MILLIS; setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 200 * HOUR_IN_MILLIS); setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, "500=" + HOUR_IN_MILLIS + ",400=" + 25 * HOUR_IN_MILLIS + ",300=" + 50 * HOUR_IN_MILLIS + ",300=" + fallbackDuration + ",200=" + 100 * HOUR_IN_MILLIS + ",100=" + 200 * HOUR_IN_MILLIS); long nextTimeToDropNumConstraints; // no delay, deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(MIN_WINDOW_FOR_FLEXIBILITY_MS); JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); assertEquals(JobStatus.NO_EARLIEST_RUNTIME, js.getEarliestRunTime()); assertEquals(MIN_WINDOW_FOR_FLEXIBILITY_MS + FROZEN_TIME, js.getLatestRunTimeElapsed()); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, js.getLatestRunTimeElapsed()); assertEquals(FROZEN_TIME, js.enqueueTime); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 6, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 7, nextTimeToDropNumConstraints); // delay, no deadline Loading Loading @@ -574,81 +576,83 @@ public class FlexibilityControllerTest { // delay, deadline jb = createJob(0) .setOverrideDeadline(2 * MIN_WINDOW_FOR_FLEXIBILITY_MS) .setMinimumLatency(MIN_WINDOW_FOR_FLEXIBILITY_MS); .setOverrideDeadline(2 * HOUR_IN_MILLIS) .setMinimumLatency(HOUR_IN_MILLIS); js = createJobStatus("time", jb); final long windowStart = FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS; final long windowStart = FROZEN_TIME + HOUR_IN_MILLIS; nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, assertEquals(windowStart + fallbackDuration / 10 * 5, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, assertEquals(windowStart + fallbackDuration / 10 * 6, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, assertEquals(windowStart + fallbackDuration / 10 * 7, nextTimeToDropNumConstraints); } @Test public void testCurPercent() { final long fallbackDuration = 10 * HOUR_IN_MILLIS; setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, "300=" + fallbackDuration); long deadline = 100 * MINUTE_IN_MILLIS; long nowElapsed = FROZEN_TIME; JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline); JobStatus js = createJobStatus("time", jb); assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); assertEquals(deadline + FROZEN_TIME, assertEquals(FROZEN_TIME + fallbackDuration, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, FROZEN_TIME)); nowElapsed = FROZEN_TIME + 60 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 6 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 130 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 95 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 9 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); long delay = MINUTE_IN_MILLIS; deadline = 101 * MINUTE_IN_MILLIS; long delay = HOUR_IN_MILLIS; deadline = HOUR_IN_MILLIS + 100 * MINUTE_IN_MILLIS; jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay); js = createJobStatus("time", jb); assertEquals(FROZEN_TIME + delay, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); assertEquals(deadline + FROZEN_TIME, assertEquals(FROZEN_TIME + delay + fallbackDuration, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, FROZEN_TIME + delay)); nowElapsed = FROZEN_TIME + delay + 60 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + delay + 6 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 130 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + delay + 95 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + delay + 9 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); } @Test Loading Loading @@ -786,26 +790,27 @@ public class FlexibilityControllerTest { // deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); assertEquals(3 * HOUR_IN_MILLIS + js.enqueueTime, mFlexibilityController .getLifeCycleEndElapsedLocked(js, nowElapsed, js.enqueueTime)); // no deadline assertEquals(FROZEN_TIME + 2 * HOUR_IN_MILLIS, assertEquals(js.enqueueTime + 2 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_HIGH)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 3 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 3 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 4 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 4 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_LOW)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 5 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 5 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_MIN)), nowElapsed, 100L)); nowElapsed, js.enqueueTime)); } @Test Loading Loading @@ -871,14 +876,16 @@ public class FlexibilityControllerTest { mFlexibilityController.prepareForExecutionLocked(jsLow); mFlexibilityController.prepareForExecutionLocked(jsMin); // deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", jb); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); final long longDeadlineMs = 14 * 24 * HOUR_IN_MILLIS; JobInfo.Builder jbWithLongDeadline = createJob(0).setOverrideDeadline(longDeadlineMs); JobStatus jsWithLongDeadline = createJobStatus( "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithLongDeadline); JobInfo.Builder jbWithShortDeadline = createJob(0).setOverrideDeadline(15 * MINUTE_IN_MILLIS); JobStatus jsWithShortDeadline = createJobStatus( "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithShortDeadline); final long earliestMs = 123L; // no deadline assertEquals(earliestMs + HOUR_IN_MILLIS + 5 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", Loading @@ -894,6 +901,9 @@ public class FlexibilityControllerTest { createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), nowElapsed, earliestMs)); assertEquals(earliestMs + HOUR_IN_MILLIS + 3 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( jsWithShortDeadline, nowElapsed, earliestMs)); assertEquals(earliestMs + HOUR_IN_MILLIS + 2 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", Loading @@ -904,6 +914,9 @@ public class FlexibilityControllerTest { createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", createJob(0).setPriority(JobInfo.PRIORITY_MIN)), nowElapsed, earliestMs)); assertEquals(jsWithLongDeadline.enqueueTime + longDeadlineMs, mFlexibilityController.getLifeCycleEndElapsedLocked( jsWithLongDeadline, nowElapsed, earliestMs)); } @Test Loading Loading @@ -1033,8 +1046,8 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0); jb.setMinimumLatency(1); jb.setOverrideDeadline(2); JobStatus js = createJobStatus("Disable Flexible When Job Has Short Window", jb); assertFalse(js.hasFlexibilityConstraint()); JobStatus js = createJobStatus("testExceptions_ShortWindow", jb); assertTrue(js.hasFlexibilityConstraint()); } @Test Loading Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java +58 −23 Original line number Diff line number Diff line Loading @@ -241,6 +241,8 @@ public final class FlexibilityController extends StateController { private static final long MAX_TIME_WINDOW_MS = 24 * HOUR_IN_MILLIS; private final JobScoreBucket[] mScoreBuckets = new JobScoreBucket[NUM_SCORE_BUCKETS]; private int mScoreBucketIndex = 0; private long mCachedScoreExpirationTimeElapsed; private int mCachedScore; public void addScore(int add, long nowElapsed) { JobScoreBucket bucket = mScoreBuckets[mScoreBucketIndex]; Loading @@ -248,10 +250,17 @@ public final class FlexibilityController extends StateController { bucket = new JobScoreBucket(); bucket.startTimeElapsed = nowElapsed; mScoreBuckets[mScoreBucketIndex] = bucket; // Brand new bucket, there's nothing to remove from the score, // so just update the expiration time if needed. mCachedScoreExpirationTimeElapsed = Math.min(mCachedScoreExpirationTimeElapsed, nowElapsed + MAX_TIME_WINDOW_MS); } else if (bucket.startTimeElapsed < nowElapsed - MAX_TIME_WINDOW_MS) { // The bucket is too old. bucket.reset(); bucket.startTimeElapsed = nowElapsed; // Force a recalculation of the cached score instead of just updating the cached // value and time in case there are multiple stale buckets. mCachedScoreExpirationTimeElapsed = nowElapsed; } else if (bucket.startTimeElapsed < nowElapsed - MAX_TIME_WINDOW_MS / NUM_SCORE_BUCKETS) { // The current bucket's duration has completed. Move on to the next bucket. Loading @@ -261,16 +270,26 @@ public final class FlexibilityController extends StateController { } bucket.score += add; mCachedScore += add; } public int getScore(long nowElapsed) { if (nowElapsed < mCachedScoreExpirationTimeElapsed) { return mCachedScore; } int score = 0; final long earliestElapsed = nowElapsed - MAX_TIME_WINDOW_MS; long earliestValidBucketTimeElapsed = Long.MAX_VALUE; for (JobScoreBucket bucket : mScoreBuckets) { if (bucket != null && bucket.startTimeElapsed >= earliestElapsed) { score += bucket.score; if (earliestValidBucketTimeElapsed > bucket.startTimeElapsed) { earliestValidBucketTimeElapsed = bucket.startTimeElapsed; } } } mCachedScore = score; mCachedScoreExpirationTimeElapsed = earliestValidBucketTimeElapsed + MAX_TIME_WINDOW_MS; return score; } Loading Loading @@ -378,10 +397,16 @@ public final class FlexibilityController extends StateController { @Override public void prepareForExecutionLocked(JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) { // Don't include jobs for the TOP app in the score calculation. return; } // Use the job's requested priority to determine its score since that is what the developer // selected and it will be stable across job runs. final int score = mFallbackFlexibilityDeadlineScores .get(jobStatus.getJob().getPriority(), jobStatus.getJob().getPriority() / 100); final int priority = jobStatus.getJob().getPriority(); final int score = mFallbackFlexibilityDeadlineScores.get(priority, FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_SCORES .get(priority, priority / 100)); JobScoreTracker jobScoreTracker = mJobScoreTrackers.get(jobStatus.getSourceUid(), jobStatus.getSourcePackageName()); if (jobScoreTracker == null) { Loading @@ -394,6 +419,10 @@ public final class FlexibilityController extends StateController { @Override public void unprepareFromExecutionLocked(JobStatus jobStatus) { if (jobStatus.lastEvaluatedBias == JobInfo.BIAS_TOP_APP) { // Jobs for the TOP app are excluded from the score calculation. return; } // The job didn't actually start. Undo the score increase. JobScoreTracker jobScoreTracker = mJobScoreTrackers.get(jobStatus.getSourceUid(), jobStatus.getSourcePackageName()); Loading @@ -401,8 +430,10 @@ public final class FlexibilityController extends StateController { Slog.e(TAG, "Unprepared a job that didn't result in a score change"); return; } final int score = mFallbackFlexibilityDeadlineScores .get(jobStatus.getJob().getPriority(), jobStatus.getJob().getPriority() / 100); final int priority = jobStatus.getJob().getPriority(); final int score = mFallbackFlexibilityDeadlineScores.get(priority, FcConfig.DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_SCORES .get(priority, priority / 100)); jobScoreTracker.addScore(-score, sElapsedRealtimeClock.millis()); } Loading Loading @@ -649,7 +680,7 @@ public final class FlexibilityController extends StateController { (long) Math.scalb(mRescheduledJobDeadline, js.getNumPreviousAttempts() - 2), mMaxRescheduledDeadline); } if (js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME) { // Intentionally use the effective priority here. If a job's priority was effectively // lowered, it will be less likely to run quickly given other policies in JobScheduler. // Thus, there's no need to further delay the job based on flex policy. Loading @@ -657,13 +688,16 @@ public final class FlexibilityController extends StateController { final int jobScore = getScoreLocked(js.getSourceUid(), js.getSourcePackageName(), nowElapsed); // Set an upper limit on the fallback deadline so that the delay doesn't become extreme. final long fallbackDeadlineMs = Math.min(3 * mFallbackFlexibilityDeadlineMs, final long fallbackDurationMs = Math.min(3 * mFallbackFlexibilityDeadlineMs, mFallbackFlexibilityDeadlines.get(jobPriority, mFallbackFlexibilityDeadlineMs) + mFallbackFlexibilityAdditionalScoreTimeFactors .get(jobPriority, MINUTE_IN_MILLIS) * jobScore); return earliest + fallbackDeadlineMs; final long fallbackDeadlineMs = earliest + fallbackDurationMs; if (js.getLatestRunTimeElapsed() == JobStatus.NO_LATEST_RUNTIME) { return fallbackDeadlineMs; } return js.getLatestRunTimeElapsed(); return Math.max(fallbackDeadlineMs, js.getLatestRunTimeElapsed()); } @VisibleForTesting Loading Loading @@ -976,7 +1010,8 @@ public final class FlexibilityController extends StateController { // Something has gone horribly wrong. This has only occurred on incorrectly // configured tests, but add a check here for safety. Slog.wtf(TAG, "Got invalid latest when scheduling alarm." + " Prefetch=" + js.getJob().isPrefetch()); + " prefetch=" + js.getJob().isPrefetch() + " periodic=" + js.getJob().isPeriodic()); // Since things have gone wrong, the safest and most reliable thing to do is // stop applying flex policy to the job. mFlexibilityTracker.setNumDroppedFlexibleConstraints(js, Loading @@ -991,7 +1026,7 @@ public final class FlexibilityController extends StateController { if (DEBUG) { Slog.d(TAG, "scheduleDropNumConstraintsAlarm: " + js.getSourcePackageName() + " " + js.getSourceUserId() + js.toShortString() + " numApplied: " + js.getNumAppliedFlexibleConstraints() + " numRequired: " + js.getNumRequiredFlexibleConstraints() + " numSatisfied: " + Integer.bitCount( Loading Loading @@ -1199,11 +1234,11 @@ public final class FlexibilityController extends StateController { DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_MAX, 0); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_HIGH, 4 * MINUTE_IN_MILLIS); .put(PRIORITY_HIGH, 3 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_DEFAULT, 3 * MINUTE_IN_MILLIS); .put(PRIORITY_DEFAULT, 2 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_LOW, 2 * MINUTE_IN_MILLIS); .put(PRIORITY_LOW, 1 * MINUTE_IN_MILLIS); DEFAULT_FALLBACK_FLEXIBILITY_DEADLINE_ADDITIONAL_SCORE_TIME_FACTORS .put(PRIORITY_MIN, 1 * MINUTE_IN_MILLIS); DEFAULT_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS Loading @@ -1220,7 +1255,7 @@ public final class FlexibilityController extends StateController { private static final long DEFAULT_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS = MINUTE_IN_MILLIS; private static final long DEFAULT_RESCHEDULED_JOB_DEADLINE_MS = HOUR_IN_MILLIS; private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = 5 * DAY_IN_MILLIS; private static final long DEFAULT_MAX_RESCHEDULED_DEADLINE_MS = DAY_IN_MILLIS; @VisibleForTesting static final long DEFAULT_UNSEEN_CONSTRAINT_GRACE_PERIOD_MS = 3 * DAY_IN_MILLIS; Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +0 −9 Original line number Diff line number Diff line Loading @@ -16,8 +16,6 @@ package com.android.server.job.controllers; import static android.text.format.DateUtils.HOUR_IN_MILLIS; import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX; import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX; import static com.android.server.job.JobSchedulerService.NEVER_INDEX; Loading Loading @@ -430,9 +428,6 @@ public final class JobStatus { */ public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2; /** Minimum difference between start and end time to have flexible constraint */ @VisibleForTesting static final long MIN_WINDOW_FOR_FLEXIBILITY_MS = HOUR_IN_MILLIS; /** * Versatile, persistable flags for a job that's updated within the system server, * as opposed to {@link JobInfo#flags} that's set by callers. Loading Loading @@ -708,14 +703,10 @@ public final class JobStatus { final boolean lacksSomeFlexibleConstraints = ((~requiredConstraints) & SYSTEM_WIDE_FLEXIBLE_CONSTRAINTS) != 0 || mCanApplyTransportAffinities; final boolean satisfiesMinWindowException = (latestRunTimeElapsedMillis - earliestRunTimeElapsedMillis) >= MIN_WINDOW_FOR_FLEXIBILITY_MS; // The first time a job is rescheduled it will not be subject to flexible constraints. // Otherwise, every consecutive reschedule increases a jobs' flexibility deadline. if (!isRequestedExpeditedJob() && !job.isUserInitiated() && satisfiesMinWindowException && (numFailures + numSystemStops) != 1 && lacksSomeFlexibleConstraints) { requiredConstraints |= CONSTRAINT_FLEXIBLE; Loading
services/tests/mockingservicestests/src/com/android/server/job/controllers/FlexibilityControllerTest.java +61 −48 Original line number Diff line number Diff line Loading @@ -49,7 +49,6 @@ import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONNECTIVI import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CONTENT_TRIGGER; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_FLEXIBLE; import static com.android.server.job.controllers.JobStatus.CONSTRAINT_IDLE; import static com.android.server.job.controllers.JobStatus.MIN_WINDOW_FOR_FLEXIBILITY_MS; import static com.android.server.job.controllers.JobStatus.NO_LATEST_RUNTIME; import static org.junit.Assert.assertArrayEquals; Loading Loading @@ -410,10 +409,12 @@ public class FlexibilityControllerTest { @Test public void testOnConstantsUpdated_PercentsToDropConstraints() { final long fallbackDuration = 12 * HOUR_IN_MILLIS; JobInfo.Builder jb = createJob(0) .setOverrideDeadline(MIN_WINDOW_FOR_FLEXIBILITY_MS); .setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("testPercentsToDropConstraintsConfig", jb); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, // Even though the override deadline is 1 hour, the fallback duration is still used. assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); setDeviceConfigString(KEY_PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS, "500=1|2|3|4" Loading Loading @@ -441,13 +442,13 @@ public class FlexibilityControllerTest { mFlexibilityController.mFcConfig.PERCENTS_TO_DROP_FLEXIBLE_CONSTRAINTS .get(JobInfo.PRIORITY_MIN), new int[]{54, 55, 56, 57}); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10, assertEquals(FROZEN_TIME + fallbackDuration / 10, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); js.setNumDroppedFlexibleConstraints(1); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 2, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 2, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); js.setNumDroppedFlexibleConstraints(2); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 3, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 3, mFlexibilityController.getNextConstraintDropTimeElapsedLocked(js)); } Loading Loading @@ -504,37 +505,38 @@ public class FlexibilityControllerTest { @Test public void testGetNextConstraintDropTimeElapsedLocked() { final long fallbackDuration = 50 * HOUR_IN_MILLIS; setDeviceConfigLong(KEY_FALLBACK_FLEXIBILITY_DEADLINE, 200 * HOUR_IN_MILLIS); setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, "500=" + HOUR_IN_MILLIS + ",400=" + 25 * HOUR_IN_MILLIS + ",300=" + 50 * HOUR_IN_MILLIS + ",300=" + fallbackDuration + ",200=" + 100 * HOUR_IN_MILLIS + ",100=" + 200 * HOUR_IN_MILLIS); long nextTimeToDropNumConstraints; // no delay, deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(MIN_WINDOW_FOR_FLEXIBILITY_MS); JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); assertEquals(JobStatus.NO_EARLIEST_RUNTIME, js.getEarliestRunTime()); assertEquals(MIN_WINDOW_FOR_FLEXIBILITY_MS + FROZEN_TIME, js.getLatestRunTimeElapsed()); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, js.getLatestRunTimeElapsed()); assertEquals(FROZEN_TIME, js.enqueueTime); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 5, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 6, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, assertEquals(FROZEN_TIME + fallbackDuration / 10 * 7, nextTimeToDropNumConstraints); // delay, no deadline Loading Loading @@ -574,81 +576,83 @@ public class FlexibilityControllerTest { // delay, deadline jb = createJob(0) .setOverrideDeadline(2 * MIN_WINDOW_FOR_FLEXIBILITY_MS) .setMinimumLatency(MIN_WINDOW_FOR_FLEXIBILITY_MS); .setOverrideDeadline(2 * HOUR_IN_MILLIS) .setMinimumLatency(HOUR_IN_MILLIS); js = createJobStatus("time", jb); final long windowStart = FROZEN_TIME + MIN_WINDOW_FOR_FLEXIBILITY_MS; final long windowStart = FROZEN_TIME + HOUR_IN_MILLIS; nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 5, assertEquals(windowStart + fallbackDuration / 10 * 5, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(1); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 6, assertEquals(windowStart + fallbackDuration / 10 * 6, nextTimeToDropNumConstraints); js.setNumDroppedFlexibleConstraints(2); nextTimeToDropNumConstraints = mFlexibilityController .getNextConstraintDropTimeElapsedLocked(js); assertEquals(windowStart + MIN_WINDOW_FOR_FLEXIBILITY_MS / 10 * 7, assertEquals(windowStart + fallbackDuration / 10 * 7, nextTimeToDropNumConstraints); } @Test public void testCurPercent() { final long fallbackDuration = 10 * HOUR_IN_MILLIS; setDeviceConfigString(KEY_FALLBACK_FLEXIBILITY_DEADLINES, "300=" + fallbackDuration); long deadline = 100 * MINUTE_IN_MILLIS; long nowElapsed = FROZEN_TIME; JobInfo.Builder jb = createJob(0).setOverrideDeadline(deadline); JobStatus js = createJobStatus("time", jb); assertEquals(FROZEN_TIME, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); assertEquals(deadline + FROZEN_TIME, assertEquals(FROZEN_TIME + fallbackDuration, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, FROZEN_TIME)); nowElapsed = FROZEN_TIME + 60 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 6 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 130 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 95 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 9 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); long delay = MINUTE_IN_MILLIS; deadline = 101 * MINUTE_IN_MILLIS; long delay = HOUR_IN_MILLIS; deadline = HOUR_IN_MILLIS + 100 * MINUTE_IN_MILLIS; jb = createJob(0).setOverrideDeadline(deadline).setMinimumLatency(delay); js = createJobStatus("time", jb); assertEquals(FROZEN_TIME + delay, mFlexibilityController.getLifeCycleBeginningElapsedLocked(js)); assertEquals(deadline + FROZEN_TIME, assertEquals(FROZEN_TIME + delay + fallbackDuration, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, FROZEN_TIME + delay)); nowElapsed = FROZEN_TIME + delay + 60 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + delay + 6 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(60, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + 130 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + 13 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(100, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); nowElapsed = FROZEN_TIME + delay + 95 * MINUTE_IN_MILLIS; nowElapsed = FROZEN_TIME + delay + 9 * HOUR_IN_MILLIS; JobSchedulerService.sElapsedRealtimeClock = Clock.fixed(Instant.ofEpochMilli(nowElapsed), ZoneOffset.UTC); assertEquals(95, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); assertEquals(90, mFlexibilityController.getCurPercentOfLifecycleLocked(js, nowElapsed)); } @Test Loading Loading @@ -786,26 +790,27 @@ public class FlexibilityControllerTest { // deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("time", jb); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); assertEquals(3 * HOUR_IN_MILLIS + js.enqueueTime, mFlexibilityController .getLifeCycleEndElapsedLocked(js, nowElapsed, js.enqueueTime)); // no deadline assertEquals(FROZEN_TIME + 2 * HOUR_IN_MILLIS, assertEquals(js.enqueueTime + 2 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_HIGH)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 3 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 3 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 4 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 4 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_LOW)), nowElapsed, 100L)); assertEquals(FROZEN_TIME + 5 * HOUR_IN_MILLIS, nowElapsed, js.enqueueTime)); assertEquals(js.enqueueTime + 5 * HOUR_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("time", createJob(0).setPriority(JobInfo.PRIORITY_MIN)), nowElapsed, 100L)); nowElapsed, js.enqueueTime)); } @Test Loading Loading @@ -871,14 +876,16 @@ public class FlexibilityControllerTest { mFlexibilityController.prepareForExecutionLocked(jsLow); mFlexibilityController.prepareForExecutionLocked(jsMin); // deadline JobInfo.Builder jb = createJob(0).setOverrideDeadline(HOUR_IN_MILLIS); JobStatus js = createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", jb); assertEquals(HOUR_IN_MILLIS + FROZEN_TIME, mFlexibilityController.getLifeCycleEndElapsedLocked(js, nowElapsed, 0)); final long longDeadlineMs = 14 * 24 * HOUR_IN_MILLIS; JobInfo.Builder jbWithLongDeadline = createJob(0).setOverrideDeadline(longDeadlineMs); JobStatus jsWithLongDeadline = createJobStatus( "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithLongDeadline); JobInfo.Builder jbWithShortDeadline = createJob(0).setOverrideDeadline(15 * MINUTE_IN_MILLIS); JobStatus jsWithShortDeadline = createJobStatus( "testGetLifeCycleEndElapsedLocked_ScoreAddition", jbWithShortDeadline); final long earliestMs = 123L; // no deadline assertEquals(earliestMs + HOUR_IN_MILLIS + 5 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", Loading @@ -894,6 +901,9 @@ public class FlexibilityControllerTest { createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", createJob(0).setPriority(JobInfo.PRIORITY_DEFAULT)), nowElapsed, earliestMs)); assertEquals(earliestMs + HOUR_IN_MILLIS + 3 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( jsWithShortDeadline, nowElapsed, earliestMs)); assertEquals(earliestMs + HOUR_IN_MILLIS + 2 * 15 * MINUTE_IN_MILLIS, mFlexibilityController.getLifeCycleEndElapsedLocked( createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", Loading @@ -904,6 +914,9 @@ public class FlexibilityControllerTest { createJobStatus("testGetLifeCycleEndElapsedLocked_ScoreAddition", createJob(0).setPriority(JobInfo.PRIORITY_MIN)), nowElapsed, earliestMs)); assertEquals(jsWithLongDeadline.enqueueTime + longDeadlineMs, mFlexibilityController.getLifeCycleEndElapsedLocked( jsWithLongDeadline, nowElapsed, earliestMs)); } @Test Loading Loading @@ -1033,8 +1046,8 @@ public class FlexibilityControllerTest { JobInfo.Builder jb = createJob(0); jb.setMinimumLatency(1); jb.setOverrideDeadline(2); JobStatus js = createJobStatus("Disable Flexible When Job Has Short Window", jb); assertFalse(js.hasFlexibilityConstraint()); JobStatus js = createJobStatus("testExceptions_ShortWindow", jb); assertTrue(js.hasFlexibilityConstraint()); } @Test Loading