Loading apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +27 −3 Original line number Original line Diff line number Diff line Loading @@ -482,6 +482,7 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_RUNTIME_UI_LIMIT_MS: case Constants.KEY_RUNTIME_UI_LIMIT_MS: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS: case Constants.KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS: case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS: case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS: if (!runtimeUpdated) { if (!runtimeUpdated) { mConstants.updateRuntimeConstantsLocked(); mConstants.updateRuntimeConstantsLocked(); Loading Loading @@ -582,6 +583,8 @@ public class JobSchedulerService extends com.android.server.SystemService "runtime_min_ui_data_transfer_guarantee_buffer_factor"; "runtime_min_ui_data_transfer_guarantee_buffer_factor"; private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = "runtime_min_ui_data_transfer_guarantee_ms"; "runtime_min_ui_data_transfer_guarantee_ms"; private static final String KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS = "runtime_cumulative_ui_limit_ms"; private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = "runtime_use_data_estimates_for_limits"; "runtime_use_data_estimates_for_limits"; Loading Loading @@ -622,6 +625,7 @@ public class JobSchedulerService extends com.android.server.SystemService 1.35f; 1.35f; public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS); Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS); public static final long DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS = 24 * HOUR_IN_MILLIS; public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true; static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true; static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000; static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000; Loading Loading @@ -759,6 +763,12 @@ public class JobSchedulerService extends com.android.server.SystemService public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; /** * The maximum amount of cumulative time we will let a user-initiated job run for * before downgrading it. */ public long RUNTIME_CUMULATIVE_UI_LIMIT_MS = DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS; /** /** * Whether to use data estimates to determine execution limits for execution limits. * Whether to use data estimates to determine execution limits for execution limits. */ */ Loading Loading @@ -881,6 +891,7 @@ public class JobSchedulerService extends com.android.server.SystemService KEY_RUNTIME_MIN_UI_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_GUARANTEE_MS, KEY_RUNTIME_UI_LIMIT_MS, KEY_RUNTIME_UI_LIMIT_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); // Make sure min runtime for regular jobs is at least 10 minutes. // Make sure min runtime for regular jobs is at least 10 minutes. Loading Loading @@ -915,6 +926,11 @@ public class JobSchedulerService extends com.android.server.SystemService properties.getLong( properties.getLong( KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); // The cumulative runtime limit should be at least the max execution limit. RUNTIME_CUMULATIVE_UI_LIMIT_MS = Math.max(RUNTIME_UI_LIMIT_MS, properties.getLong( KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS)); RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean( RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean( KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, Loading Loading @@ -972,6 +988,7 @@ public class JobSchedulerService extends com.android.server.SystemService RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println(); RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println(); pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println(); RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, RUNTIME_CUMULATIVE_UI_LIMIT_MS).println(); pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println(); RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println(); Loading Loading @@ -2452,11 +2469,16 @@ public class JobSchedulerService extends com.android.server.SystemService JobStatus newJob = new JobStatus(failureToReschedule, JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, elapsedNowMillis + delayMillis, JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis()); failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(), failureToReschedule.getCumulativeExecutionTimeMs()); if (stopReason == JobParameters.STOP_REASON_USER) { if (stopReason == JobParameters.STOP_REASON_USER) { // Demote all jobs to regular for user stops so they don't keep privileges. // Demote all jobs to regular for user stops so they don't keep privileges. newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); } } if (newJob.getCumulativeExecutionTimeMs() >= mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS && newJob.shouldTreatAsUserInitiatedJob()) { newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); } if (job.isPeriodic()) { if (job.isPeriodic()) { newJob.setOriginalLatestRunTimeElapsed( newJob.setOriginalLatestRunTimeElapsed( failureToReschedule.getOriginalLatestRunTimeElapsed()); failureToReschedule.getOriginalLatestRunTimeElapsed()); Loading Loading @@ -2548,7 +2570,8 @@ public class JobSchedulerService extends com.android.server.SystemService elapsedNow + period - flex, elapsedNow + period, elapsedNow + period - flex, elapsedNow + period, 0 /* numFailures */, 0 /* numSystemStops */, 0 /* numFailures */, 0 /* numSystemStops */, sSystemClock.millis() /* lastSuccessfulRunTime */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime()); periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); } } final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed Loading @@ -2563,7 +2586,8 @@ public class JobSchedulerService extends com.android.server.SystemService newEarliestRunTimeElapsed, newLatestRuntimeElapsed, newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 0 /* numFailures */, 0 /* numSystemStops */, 0 /* numFailures */, 0 /* numSystemStops */, sSystemClock.millis() /* lastSuccessfulRunTime */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime()); periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); } } // JobCompletedListener implementations. // JobCompletedListener implementations. Loading apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +18 −2 Original line number Original line Diff line number Diff line Loading @@ -106,6 +106,7 @@ public final class JobServiceContext implements ServiceConnection { private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; /** Amount of time the JobScheduler will wait for a job to provide a required notification. */ /** Amount of time the JobScheduler will wait for a job to provide a required notification. */ private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER; private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER; private static final long EXECUTION_DURATION_STAMP_PERIOD_MILLIS = 5 * 60_000L; private static final String[] VERB_STRINGS = { private static final String[] VERB_STRINGS = { "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED" "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED" Loading Loading @@ -191,6 +192,8 @@ public final class JobServiceContext implements ServiceConnection { private long mMaxExecutionTimeMillis; private long mMaxExecutionTimeMillis; /** Whether this job is required to provide a notification and we're still waiting for it. */ /** Whether this job is required to provide a notification and we're still waiting for it. */ private boolean mAwaitingNotification; private boolean mAwaitingNotification; /** The last time we updated the job's execution duration, in the elapsed realtime timebase. */ private long mLastExecutionDurationStampTimeElapsed; private long mEstimatedDownloadBytes; private long mEstimatedDownloadBytes; private long mEstimatedUploadBytes; private long mEstimatedUploadBytes; Loading Loading @@ -1245,7 +1248,15 @@ public final class JobServiceContext implements ServiceConnection { /* anrMessage */ "required notification not provided", /* anrMessage */ "required notification not provided", /* triggerAnr */ true); /* triggerAnr */ true); } else { } else { final long timeSinceDurationStampTimeMs = nowElapsed - mLastExecutionDurationStampTimeElapsed; if (timeSinceDurationStampTimeMs < EXECUTION_DURATION_STAMP_PERIOD_MILLIS) { Slog.e(TAG, "Unexpected op timeout while EXECUTING"); Slog.e(TAG, "Unexpected op timeout while EXECUTING"); } // Update the execution time even if this wasn't the pre-set time. mRunningJob.incrementCumulativeExecutionTime(timeSinceDurationStampTimeMs); mService.mJobs.touchJob(mRunningJob); mLastExecutionDurationStampTimeElapsed = nowElapsed; scheduleOpTimeOutLocked(); scheduleOpTimeOutLocked(); } } break; break; Loading Loading @@ -1314,8 +1325,11 @@ public final class JobServiceContext implements ServiceConnection { Slog.d(TAG, "Cleaning up " + mRunningJob.toShortString() Slog.d(TAG, "Cleaning up " + mRunningJob.toShortString() + " reschedule=" + reschedule + " reason=" + loggingDebugReason); + " reschedule=" + reschedule + " reason=" + loggingDebugReason); } } final long nowElapsed = sElapsedRealtimeClock.millis(); applyStoppedReasonLocked(loggingDebugReason); applyStoppedReasonLocked(loggingDebugReason); completedJob = mRunningJob; completedJob = mRunningJob; completedJob.incrementCumulativeExecutionTime( nowElapsed - mLastExecutionDurationStampTimeElapsed); // Use the JobParameters stop reasons for logging and metric purposes, // Use the JobParameters stop reasons for logging and metric purposes, // but if the job was marked for death, use that reason for rescheduling purposes. // but if the job was marked for death, use that reason for rescheduling purposes. // The discrepancy could happen if a job ends up stopping for some reason // The discrepancy could happen if a job ends up stopping for some reason Loading @@ -1342,7 +1356,7 @@ public final class JobServiceContext implements ServiceConnection { mPreviousJobHadSuccessfulFinish = mPreviousJobHadSuccessfulFinish = (loggingInternalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); (loggingInternalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); if (!mPreviousJobHadSuccessfulFinish) { if (!mPreviousJobHadSuccessfulFinish) { mLastUnsuccessfulFinishElapsed = sElapsedRealtimeClock.millis(); mLastUnsuccessfulFinishElapsed = nowElapsed; } } mJobPackageTracker.noteInactive(completedJob, mJobPackageTracker.noteInactive(completedJob, loggingInternalStopReason, loggingDebugReason); loggingInternalStopReason, loggingDebugReason); Loading Loading @@ -1411,6 +1425,7 @@ public final class JobServiceContext implements ServiceConnection { mDeathMarkStopReason = JobParameters.STOP_REASON_UNDEFINED; mDeathMarkStopReason = JobParameters.STOP_REASON_UNDEFINED; mDeathMarkInternalStopReason = 0; mDeathMarkInternalStopReason = 0; mDeathMarkDebugReason = null; mDeathMarkDebugReason = null; mLastExecutionDurationStampTimeElapsed = 0; mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingInternalStopReason = 0; mPendingInternalStopReason = 0; mPendingDebugStopReason = null; mPendingDebugStopReason = null; Loading Loading @@ -1460,6 +1475,7 @@ public final class JobServiceContext implements ServiceConnection { if (mAwaitingNotification) { if (mAwaitingNotification) { minTimeout = Math.min(minTimeout, NOTIFICATION_TIMEOUT_MILLIS); minTimeout = Math.min(minTimeout, NOTIFICATION_TIMEOUT_MILLIS); } } minTimeout = Math.min(minTimeout, EXECUTION_DURATION_STAMP_PERIOD_MILLIS); timeoutMillis = minTimeout; timeoutMillis = minTimeout; break; break; Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +11 −3 Original line number Original line Diff line number Diff line Loading @@ -236,7 +236,8 @@ public final class JobStore { convertRtcBoundsToElapsed(utcTimes, elapsedNow); convertRtcBoundsToElapsed(utcTimes, elapsedNow); JobStatus newJob = new JobStatus(job, JobStatus newJob = new JobStatus(job, elapsedRuntimes.first, elapsedRuntimes.second, elapsedRuntimes.first, elapsedRuntimes.second, 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime()); 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime(), job.getCumulativeExecutionTimeMs()); newJob.prepareLocked(); newJob.prepareLocked(); toAdd.add(newJob); toAdd.add(newJob); toRemove.add(job); toRemove.add(job); Loading Loading @@ -786,7 +787,7 @@ public final class JobStore { * Write out a tag with data comprising the required fields and bias of this job and * Write out a tag with data comprising the required fields and bias of this job and * its client. * its client. */ */ private void addAttributesToJobTag(XmlSerializer out, JobStatus jobStatus) private void addAttributesToJobTag(TypedXmlSerializer out, JobStatus jobStatus) throws IOException { throws IOException { out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); Loading @@ -813,6 +814,9 @@ public final class JobStore { String.valueOf(jobStatus.getLastSuccessfulRunTime())); String.valueOf(jobStatus.getLastSuccessfulRunTime())); out.attribute(null, "lastFailedRunTime", out.attribute(null, "lastFailedRunTime", String.valueOf(jobStatus.getLastFailedRunTime())); String.valueOf(jobStatus.getLastFailedRunTime())); out.attributeLong(null, "cumulativeExecutionTime", jobStatus.getCumulativeExecutionTimeMs()); } } private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) Loading Loading @@ -1190,6 +1194,7 @@ public final class JobStore { int uid, sourceUserId; int uid, sourceUserId; long lastSuccessfulRunTime; long lastSuccessfulRunTime; long lastFailedRunTime; long lastFailedRunTime; long cumulativeExecutionTime; int internalFlags = 0; int internalFlags = 0; // Read out job identifier attributes and bias. // Read out job identifier attributes and bias. Loading Loading @@ -1230,6 +1235,9 @@ public final class JobStore { val = parser.getAttributeValue(null, "lastFailedRunTime"); val = parser.getAttributeValue(null, "lastFailedRunTime"); lastFailedRunTime = val == null ? 0 : Long.parseLong(val); lastFailedRunTime = val == null ? 0 : Long.parseLong(val); cumulativeExecutionTime = parser.getAttributeLong(null, "cumulativeExecutionTime", 0); } catch (NumberFormatException e) { } catch (NumberFormatException e) { Slog.e(TAG, "Error parsing job's required fields, skipping"); Slog.e(TAG, "Error parsing job's required fields, skipping"); return null; return null; Loading Loading @@ -1402,7 +1410,7 @@ public final class JobStore { builtJob, uid, sourcePackageName, sourceUserId, builtJob, uid, sourcePackageName, sourceUserId, appBucket, namespace, sourceTag, appBucket, namespace, sourceTag, elapsedRuntimes.first, elapsedRuntimes.second, elapsedRuntimes.first, elapsedRuntimes.second, lastSuccessfulRunTime, lastFailedRunTime, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTime, (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0); (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0); if (jobWorkItems != null) { if (jobWorkItems != null) { for (int i = 0; i < jobWorkItems.size(); ++i) { for (int i = 0; i < jobWorkItems.size(); ++i) { Loading apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +39 −5 Original line number Original line Diff line number Diff line Loading @@ -375,6 +375,11 @@ public final class JobStatus { * and is thus considered demoted from whatever privileged state it had in the past. * and is thus considered demoted from whatever privileged state it had in the past. */ */ public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1; public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1; /** * Flag for {@link #mInternalFlags}: this job is demoted by the system * from running as a user-initiated job. */ public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2; /** Minimum difference between start and end time to have flexible constraint */ /** Minimum difference between start and end time to have flexible constraint */ @VisibleForTesting @VisibleForTesting Loading @@ -385,6 +390,12 @@ public final class JobStatus { */ */ private int mInternalFlags; private int mInternalFlags; /** * The cumulative amount of time this job has run for, including previous executions. * This is reset for periodic jobs upon a successful job execution. */ private long mCumulativeExecutionTimeMs; // These are filled in by controllers when preparing for execution. // These are filled in by controllers when preparing for execution. public ArraySet<Uri> changedUris; public ArraySet<Uri> changedUris; public ArraySet<String> changedAuthorities; public ArraySet<String> changedAuthorities; Loading Loading @@ -550,7 +561,8 @@ public final class JobStatus { int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int numFailures, int numSystemStops, int numFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, int dynamicConstraints) { int dynamicConstraints) { this.job = job; this.job = job; this.callingUid = callingUid; this.callingUid = callingUid; Loading Loading @@ -650,6 +662,8 @@ public final class JobStatus { mReadyDynamicSatisfied = false; mReadyDynamicSatisfied = false; } } mCumulativeExecutionTimeMs = cumulativeExecutionTimeMs; mLastSuccessfulRunTime = lastSuccessfulRunTime; mLastSuccessfulRunTime = lastSuccessfulRunTime; mLastFailedRunTime = lastFailedRunTime; mLastFailedRunTime = lastFailedRunTime; Loading Loading @@ -684,6 +698,7 @@ public final class JobStatus { jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getCumulativeExecutionTimeMs(), jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; if (jobStatus.mPersistedUtcTimes != null) { if (jobStatus.mPersistedUtcTimes != null) { Loading Loading @@ -711,13 +726,15 @@ public final class JobStatus { int standbyBucket, @Nullable String namespace, String sourceTag, int standbyBucket, @Nullable String namespace, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, Pair<Long, Long> persistedExecutionTimesUTC, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints) { int innerFlags, int dynamicConstraints) { this(job, callingUid, sourcePkgName, sourceUserId, this(job, callingUid, sourcePkgName, sourceUserId, standbyBucket, namespace, standbyBucket, namespace, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints); lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, innerFlags, dynamicConstraints); // Only during initial inflation do we record the UTC-timebase execution bounds // Only during initial inflation do we record the UTC-timebase execution bounds // read from the persistent store. If we ever have to recreate the JobStatus on // read from the persistent store. If we ever have to recreate the JobStatus on Loading @@ -735,14 +752,16 @@ public final class JobStatus { public JobStatus(JobStatus rescheduling, public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime) { long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs) { this(rescheduling.job, rescheduling.getUid(), this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), rescheduling.getSourceTag(), numFailures, numSystemStops, rescheduling.getSourceTag(), numFailures, numSystemStops, newEarliestRuntimeElapsedMillis, newEarliestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(), lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, rescheduling.getInternalFlags(), rescheduling.mDynamicConstraints); rescheduling.mDynamicConstraints); } } Loading Loading @@ -780,6 +799,7 @@ public final class JobStatus { standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, /* cumulativeExecutionTime */ 0, /*innerFlags=*/ 0, /* dynamicConstraints */ 0); /*innerFlags=*/ 0, /* dynamicConstraints */ 0); } } Loading Loading @@ -1312,6 +1332,14 @@ public final class JobStatus { return job.isPersisted(); return job.isPersisted(); } } public long getCumulativeExecutionTimeMs() { return mCumulativeExecutionTimeMs; } public void incrementCumulativeExecutionTime(long incrementMs) { mCumulativeExecutionTimeMs += incrementMs; } public long getEarliestRunTime() { public long getEarliestRunTime() { return earliestRunTimeElapsedMillis; return earliestRunTimeElapsedMillis; } } Loading Loading @@ -1405,7 +1433,8 @@ public final class JobStatus { */ */ public boolean shouldTreatAsUserInitiatedJob() { public boolean shouldTreatAsUserInitiatedJob() { return getJob().isUserInitiated() return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0; && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; } } /** /** Loading Loading @@ -2655,6 +2684,11 @@ public final class JobStatus { pw.print(", original latest="); pw.print(", original latest="); formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.println(); pw.println(); if (mCumulativeExecutionTimeMs != 0) { pw.print("Cumulative execution time="); TimeUtils.formatDuration(mCumulativeExecutionTimeMs, pw); pw.println(); } if (numFailures != 0) { if (numFailures != 0) { pw.print("Num failures: "); pw.println(numFailures); pw.print("Num failures: "); pw.println(numFailures); } } Loading services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +44 −0 Original line number Original line Diff line number Diff line Loading @@ -337,6 +337,50 @@ public class JobSchedulerServiceTest { mService.getMaxJobExecutionTimeMs(jobUIDT)); mService.getMaxJobExecutionTimeMs(jobUIDT)); } } /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * returns a job that is no longer allowed to run as a user-initiated job after it hits * the cumulative execution limit. */ @Test public void testGetRescheduleJobForFailure_cumulativeExecution() { JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo() .setUserInitiated(true) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); assertTrue(originalJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 0 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 50% of limit rescheduledJob.incrementCumulativeExecutionTime( mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 99.999999% of limit rescheduledJob.incrementCumulativeExecutionTime( mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2 - 1); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 100+% of limit rescheduledJob.incrementCumulativeExecutionTime(2); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); } /** /** * Confirm that * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java +27 −3 Original line number Original line Diff line number Diff line Loading @@ -482,6 +482,7 @@ public class JobSchedulerService extends com.android.server.SystemService case Constants.KEY_RUNTIME_UI_LIMIT_MS: case Constants.KEY_RUNTIME_UI_LIMIT_MS: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS: case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS: case Constants.KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS: case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS: case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS: if (!runtimeUpdated) { if (!runtimeUpdated) { mConstants.updateRuntimeConstantsLocked(); mConstants.updateRuntimeConstantsLocked(); Loading Loading @@ -582,6 +583,8 @@ public class JobSchedulerService extends com.android.server.SystemService "runtime_min_ui_data_transfer_guarantee_buffer_factor"; "runtime_min_ui_data_transfer_guarantee_buffer_factor"; private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = "runtime_min_ui_data_transfer_guarantee_ms"; "runtime_min_ui_data_transfer_guarantee_ms"; private static final String KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS = "runtime_cumulative_ui_limit_ms"; private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = "runtime_use_data_estimates_for_limits"; "runtime_use_data_estimates_for_limits"; Loading Loading @@ -622,6 +625,7 @@ public class JobSchedulerService extends com.android.server.SystemService 1.35f; 1.35f; public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS); Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS); public static final long DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS = 24 * HOUR_IN_MILLIS; public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false; static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true; static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true; static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000; static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000; Loading Loading @@ -759,6 +763,12 @@ public class JobSchedulerService extends com.android.server.SystemService public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS; /** * The maximum amount of cumulative time we will let a user-initiated job run for * before downgrading it. */ public long RUNTIME_CUMULATIVE_UI_LIMIT_MS = DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS; /** /** * Whether to use data estimates to determine execution limits for execution limits. * Whether to use data estimates to determine execution limits for execution limits. */ */ Loading Loading @@ -881,6 +891,7 @@ public class JobSchedulerService extends com.android.server.SystemService KEY_RUNTIME_MIN_UI_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_GUARANTEE_MS, KEY_RUNTIME_UI_LIMIT_MS, KEY_RUNTIME_UI_LIMIT_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS); // Make sure min runtime for regular jobs is at least 10 minutes. // Make sure min runtime for regular jobs is at least 10 minutes. Loading Loading @@ -915,6 +926,11 @@ public class JobSchedulerService extends com.android.server.SystemService properties.getLong( properties.getLong( KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS)); // The cumulative runtime limit should be at least the max execution limit. RUNTIME_CUMULATIVE_UI_LIMIT_MS = Math.max(RUNTIME_UI_LIMIT_MS, properties.getLong( KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS)); RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean( RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean( KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, Loading Loading @@ -972,6 +988,7 @@ public class JobSchedulerService extends com.android.server.SystemService RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println(); RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println(); pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS, RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println(); RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println(); pw.print(KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, RUNTIME_CUMULATIVE_UI_LIMIT_MS).println(); pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS, RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println(); RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println(); Loading Loading @@ -2452,11 +2469,16 @@ public class JobSchedulerService extends com.android.server.SystemService JobStatus newJob = new JobStatus(failureToReschedule, JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis, elapsedNowMillis + delayMillis, JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops, failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis()); failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(), failureToReschedule.getCumulativeExecutionTimeMs()); if (stopReason == JobParameters.STOP_REASON_USER) { if (stopReason == JobParameters.STOP_REASON_USER) { // Demote all jobs to regular for user stops so they don't keep privileges. // Demote all jobs to regular for user stops so they don't keep privileges. newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER); } } if (newJob.getCumulativeExecutionTimeMs() >= mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS && newJob.shouldTreatAsUserInitiatedJob()) { newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ); } if (job.isPeriodic()) { if (job.isPeriodic()) { newJob.setOriginalLatestRunTimeElapsed( newJob.setOriginalLatestRunTimeElapsed( failureToReschedule.getOriginalLatestRunTimeElapsed()); failureToReschedule.getOriginalLatestRunTimeElapsed()); Loading Loading @@ -2548,7 +2570,8 @@ public class JobSchedulerService extends com.android.server.SystemService elapsedNow + period - flex, elapsedNow + period, elapsedNow + period - flex, elapsedNow + period, 0 /* numFailures */, 0 /* numSystemStops */, 0 /* numFailures */, 0 /* numSystemStops */, sSystemClock.millis() /* lastSuccessfulRunTime */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime()); periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); } } final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed Loading @@ -2563,7 +2586,8 @@ public class JobSchedulerService extends com.android.server.SystemService newEarliestRunTimeElapsed, newLatestRuntimeElapsed, newEarliestRunTimeElapsed, newLatestRuntimeElapsed, 0 /* numFailures */, 0 /* numSystemStops */, 0 /* numFailures */, 0 /* numSystemStops */, sSystemClock.millis() /* lastSuccessfulRunTime */, sSystemClock.millis() /* lastSuccessfulRunTime */, periodicToReschedule.getLastFailedRunTime()); periodicToReschedule.getLastFailedRunTime(), 0 /* Reset cumulativeExecutionTime because of successful execution */); } } // JobCompletedListener implementations. // JobCompletedListener implementations. Loading
apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +18 −2 Original line number Original line Diff line number Diff line Loading @@ -106,6 +106,7 @@ public final class JobServiceContext implements ServiceConnection { private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; private static final long OP_TIMEOUT_MILLIS = 8 * 1000 * Build.HW_TIMEOUT_MULTIPLIER; /** Amount of time the JobScheduler will wait for a job to provide a required notification. */ /** Amount of time the JobScheduler will wait for a job to provide a required notification. */ private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER; private static final long NOTIFICATION_TIMEOUT_MILLIS = 10_000L * Build.HW_TIMEOUT_MULTIPLIER; private static final long EXECUTION_DURATION_STAMP_PERIOD_MILLIS = 5 * 60_000L; private static final String[] VERB_STRINGS = { private static final String[] VERB_STRINGS = { "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED" "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED" Loading Loading @@ -191,6 +192,8 @@ public final class JobServiceContext implements ServiceConnection { private long mMaxExecutionTimeMillis; private long mMaxExecutionTimeMillis; /** Whether this job is required to provide a notification and we're still waiting for it. */ /** Whether this job is required to provide a notification and we're still waiting for it. */ private boolean mAwaitingNotification; private boolean mAwaitingNotification; /** The last time we updated the job's execution duration, in the elapsed realtime timebase. */ private long mLastExecutionDurationStampTimeElapsed; private long mEstimatedDownloadBytes; private long mEstimatedDownloadBytes; private long mEstimatedUploadBytes; private long mEstimatedUploadBytes; Loading Loading @@ -1245,7 +1248,15 @@ public final class JobServiceContext implements ServiceConnection { /* anrMessage */ "required notification not provided", /* anrMessage */ "required notification not provided", /* triggerAnr */ true); /* triggerAnr */ true); } else { } else { final long timeSinceDurationStampTimeMs = nowElapsed - mLastExecutionDurationStampTimeElapsed; if (timeSinceDurationStampTimeMs < EXECUTION_DURATION_STAMP_PERIOD_MILLIS) { Slog.e(TAG, "Unexpected op timeout while EXECUTING"); Slog.e(TAG, "Unexpected op timeout while EXECUTING"); } // Update the execution time even if this wasn't the pre-set time. mRunningJob.incrementCumulativeExecutionTime(timeSinceDurationStampTimeMs); mService.mJobs.touchJob(mRunningJob); mLastExecutionDurationStampTimeElapsed = nowElapsed; scheduleOpTimeOutLocked(); scheduleOpTimeOutLocked(); } } break; break; Loading Loading @@ -1314,8 +1325,11 @@ public final class JobServiceContext implements ServiceConnection { Slog.d(TAG, "Cleaning up " + mRunningJob.toShortString() Slog.d(TAG, "Cleaning up " + mRunningJob.toShortString() + " reschedule=" + reschedule + " reason=" + loggingDebugReason); + " reschedule=" + reschedule + " reason=" + loggingDebugReason); } } final long nowElapsed = sElapsedRealtimeClock.millis(); applyStoppedReasonLocked(loggingDebugReason); applyStoppedReasonLocked(loggingDebugReason); completedJob = mRunningJob; completedJob = mRunningJob; completedJob.incrementCumulativeExecutionTime( nowElapsed - mLastExecutionDurationStampTimeElapsed); // Use the JobParameters stop reasons for logging and metric purposes, // Use the JobParameters stop reasons for logging and metric purposes, // but if the job was marked for death, use that reason for rescheduling purposes. // but if the job was marked for death, use that reason for rescheduling purposes. // The discrepancy could happen if a job ends up stopping for some reason // The discrepancy could happen if a job ends up stopping for some reason Loading @@ -1342,7 +1356,7 @@ public final class JobServiceContext implements ServiceConnection { mPreviousJobHadSuccessfulFinish = mPreviousJobHadSuccessfulFinish = (loggingInternalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); (loggingInternalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH); if (!mPreviousJobHadSuccessfulFinish) { if (!mPreviousJobHadSuccessfulFinish) { mLastUnsuccessfulFinishElapsed = sElapsedRealtimeClock.millis(); mLastUnsuccessfulFinishElapsed = nowElapsed; } } mJobPackageTracker.noteInactive(completedJob, mJobPackageTracker.noteInactive(completedJob, loggingInternalStopReason, loggingDebugReason); loggingInternalStopReason, loggingDebugReason); Loading Loading @@ -1411,6 +1425,7 @@ public final class JobServiceContext implements ServiceConnection { mDeathMarkStopReason = JobParameters.STOP_REASON_UNDEFINED; mDeathMarkStopReason = JobParameters.STOP_REASON_UNDEFINED; mDeathMarkInternalStopReason = 0; mDeathMarkInternalStopReason = 0; mDeathMarkDebugReason = null; mDeathMarkDebugReason = null; mLastExecutionDurationStampTimeElapsed = 0; mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingStopReason = JobParameters.STOP_REASON_UNDEFINED; mPendingInternalStopReason = 0; mPendingInternalStopReason = 0; mPendingDebugStopReason = null; mPendingDebugStopReason = null; Loading Loading @@ -1460,6 +1475,7 @@ public final class JobServiceContext implements ServiceConnection { if (mAwaitingNotification) { if (mAwaitingNotification) { minTimeout = Math.min(minTimeout, NOTIFICATION_TIMEOUT_MILLIS); minTimeout = Math.min(minTimeout, NOTIFICATION_TIMEOUT_MILLIS); } } minTimeout = Math.min(minTimeout, EXECUTION_DURATION_STAMP_PERIOD_MILLIS); timeoutMillis = minTimeout; timeoutMillis = minTimeout; break; break; Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +11 −3 Original line number Original line Diff line number Diff line Loading @@ -236,7 +236,8 @@ public final class JobStore { convertRtcBoundsToElapsed(utcTimes, elapsedNow); convertRtcBoundsToElapsed(utcTimes, elapsedNow); JobStatus newJob = new JobStatus(job, JobStatus newJob = new JobStatus(job, elapsedRuntimes.first, elapsedRuntimes.second, elapsedRuntimes.first, elapsedRuntimes.second, 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime()); 0, 0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime(), job.getCumulativeExecutionTimeMs()); newJob.prepareLocked(); newJob.prepareLocked(); toAdd.add(newJob); toAdd.add(newJob); toRemove.add(job); toRemove.add(job); Loading Loading @@ -786,7 +787,7 @@ public final class JobStore { * Write out a tag with data comprising the required fields and bias of this job and * Write out a tag with data comprising the required fields and bias of this job and * its client. * its client. */ */ private void addAttributesToJobTag(XmlSerializer out, JobStatus jobStatus) private void addAttributesToJobTag(TypedXmlSerializer out, JobStatus jobStatus) throws IOException { throws IOException { out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); out.attribute(null, "jobid", Integer.toString(jobStatus.getJobId())); out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); out.attribute(null, "package", jobStatus.getServiceComponent().getPackageName()); Loading @@ -813,6 +814,9 @@ public final class JobStore { String.valueOf(jobStatus.getLastSuccessfulRunTime())); String.valueOf(jobStatus.getLastSuccessfulRunTime())); out.attribute(null, "lastFailedRunTime", out.attribute(null, "lastFailedRunTime", String.valueOf(jobStatus.getLastFailedRunTime())); String.valueOf(jobStatus.getLastFailedRunTime())); out.attributeLong(null, "cumulativeExecutionTime", jobStatus.getCumulativeExecutionTimeMs()); } } private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) private void writeBundleToXml(PersistableBundle extras, XmlSerializer out) Loading Loading @@ -1190,6 +1194,7 @@ public final class JobStore { int uid, sourceUserId; int uid, sourceUserId; long lastSuccessfulRunTime; long lastSuccessfulRunTime; long lastFailedRunTime; long lastFailedRunTime; long cumulativeExecutionTime; int internalFlags = 0; int internalFlags = 0; // Read out job identifier attributes and bias. // Read out job identifier attributes and bias. Loading Loading @@ -1230,6 +1235,9 @@ public final class JobStore { val = parser.getAttributeValue(null, "lastFailedRunTime"); val = parser.getAttributeValue(null, "lastFailedRunTime"); lastFailedRunTime = val == null ? 0 : Long.parseLong(val); lastFailedRunTime = val == null ? 0 : Long.parseLong(val); cumulativeExecutionTime = parser.getAttributeLong(null, "cumulativeExecutionTime", 0); } catch (NumberFormatException e) { } catch (NumberFormatException e) { Slog.e(TAG, "Error parsing job's required fields, skipping"); Slog.e(TAG, "Error parsing job's required fields, skipping"); return null; return null; Loading Loading @@ -1402,7 +1410,7 @@ public final class JobStore { builtJob, uid, sourcePackageName, sourceUserId, builtJob, uid, sourcePackageName, sourceUserId, appBucket, namespace, sourceTag, appBucket, namespace, sourceTag, elapsedRuntimes.first, elapsedRuntimes.second, elapsedRuntimes.first, elapsedRuntimes.second, lastSuccessfulRunTime, lastFailedRunTime, lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTime, (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0); (rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0); if (jobWorkItems != null) { if (jobWorkItems != null) { for (int i = 0; i < jobWorkItems.size(); ++i) { for (int i = 0; i < jobWorkItems.size(); ++i) { Loading
apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +39 −5 Original line number Original line Diff line number Diff line Loading @@ -375,6 +375,11 @@ public final class JobStatus { * and is thus considered demoted from whatever privileged state it had in the past. * and is thus considered demoted from whatever privileged state it had in the past. */ */ public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1; public static final int INTERNAL_FLAG_DEMOTED_BY_USER = 1 << 1; /** * Flag for {@link #mInternalFlags}: this job is demoted by the system * from running as a user-initiated job. */ public static final int INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ = 1 << 2; /** Minimum difference between start and end time to have flexible constraint */ /** Minimum difference between start and end time to have flexible constraint */ @VisibleForTesting @VisibleForTesting Loading @@ -385,6 +390,12 @@ public final class JobStatus { */ */ private int mInternalFlags; private int mInternalFlags; /** * The cumulative amount of time this job has run for, including previous executions. * This is reset for periodic jobs upon a successful job execution. */ private long mCumulativeExecutionTimeMs; // These are filled in by controllers when preparing for execution. // These are filled in by controllers when preparing for execution. public ArraySet<Uri> changedUris; public ArraySet<Uri> changedUris; public ArraySet<String> changedAuthorities; public ArraySet<String> changedAuthorities; Loading Loading @@ -550,7 +561,8 @@ public final class JobStatus { int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int sourceUserId, int standbyBucket, @Nullable String namespace, String tag, int numFailures, int numSystemStops, int numFailures, int numSystemStops, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, int internalFlags, int dynamicConstraints) { int dynamicConstraints) { this.job = job; this.job = job; this.callingUid = callingUid; this.callingUid = callingUid; Loading Loading @@ -650,6 +662,8 @@ public final class JobStatus { mReadyDynamicSatisfied = false; mReadyDynamicSatisfied = false; } } mCumulativeExecutionTimeMs = cumulativeExecutionTimeMs; mLastSuccessfulRunTime = lastSuccessfulRunTime; mLastSuccessfulRunTime = lastSuccessfulRunTime; mLastFailedRunTime = lastFailedRunTime; mLastFailedRunTime = lastFailedRunTime; Loading Loading @@ -684,6 +698,7 @@ public final class JobStatus { jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), jobStatus.getSourceTag(), jobStatus.getNumFailures(), jobStatus.getNumSystemStops(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(), jobStatus.getCumulativeExecutionTimeMs(), jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints); mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; mPersistedUtcTimes = jobStatus.mPersistedUtcTimes; if (jobStatus.mPersistedUtcTimes != null) { if (jobStatus.mPersistedUtcTimes != null) { Loading Loading @@ -711,13 +726,15 @@ public final class JobStatus { int standbyBucket, @Nullable String namespace, String sourceTag, int standbyBucket, @Nullable String namespace, String sourceTag, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis, long lastSuccessfulRunTime, long lastFailedRunTime, long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs, Pair<Long, Long> persistedExecutionTimesUTC, Pair<Long, Long> persistedExecutionTimesUTC, int innerFlags, int dynamicConstraints) { int innerFlags, int dynamicConstraints) { this(job, callingUid, sourcePkgName, sourceUserId, this(job, callingUid, sourcePkgName, sourceUserId, standbyBucket, namespace, standbyBucket, namespace, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, sourceTag, /* numFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints); lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, innerFlags, dynamicConstraints); // Only during initial inflation do we record the UTC-timebase execution bounds // Only during initial inflation do we record the UTC-timebase execution bounds // read from the persistent store. If we ever have to recreate the JobStatus on // read from the persistent store. If we ever have to recreate the JobStatus on Loading @@ -735,14 +752,16 @@ public final class JobStatus { public JobStatus(JobStatus rescheduling, public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis, long newEarliestRuntimeElapsedMillis, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long newLatestRuntimeElapsedMillis, int numFailures, int numSystemStops, long lastSuccessfulRunTime, long lastFailedRunTime) { long lastSuccessfulRunTime, long lastFailedRunTime, long cumulativeExecutionTimeMs) { this(rescheduling.job, rescheduling.getUid(), this(rescheduling.job, rescheduling.getUid(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getSourcePackageName(), rescheduling.getSourceUserId(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), rescheduling.getStandbyBucket(), rescheduling.getNamespace(), rescheduling.getSourceTag(), numFailures, numSystemStops, rescheduling.getSourceTag(), numFailures, numSystemStops, newEarliestRuntimeElapsedMillis, newEarliestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, newLatestRuntimeElapsedMillis, lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(), lastSuccessfulRunTime, lastFailedRunTime, cumulativeExecutionTimeMs, rescheduling.getInternalFlags(), rescheduling.mDynamicConstraints); rescheduling.mDynamicConstraints); } } Loading Loading @@ -780,6 +799,7 @@ public final class JobStatus { standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, standbyBucket, namespace, tag, /* numFailures */ 0, /* numSystemStops */ 0, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, 0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */, /* cumulativeExecutionTime */ 0, /*innerFlags=*/ 0, /* dynamicConstraints */ 0); /*innerFlags=*/ 0, /* dynamicConstraints */ 0); } } Loading Loading @@ -1312,6 +1332,14 @@ public final class JobStatus { return job.isPersisted(); return job.isPersisted(); } } public long getCumulativeExecutionTimeMs() { return mCumulativeExecutionTimeMs; } public void incrementCumulativeExecutionTime(long incrementMs) { mCumulativeExecutionTimeMs += incrementMs; } public long getEarliestRunTime() { public long getEarliestRunTime() { return earliestRunTimeElapsedMillis; return earliestRunTimeElapsedMillis; } } Loading Loading @@ -1405,7 +1433,8 @@ public final class JobStatus { */ */ public boolean shouldTreatAsUserInitiatedJob() { public boolean shouldTreatAsUserInitiatedJob() { return getJob().isUserInitiated() return getJob().isUserInitiated() && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0; && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_USER) == 0 && (getInternalFlags() & INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ) == 0; } } /** /** Loading Loading @@ -2655,6 +2684,11 @@ public final class JobStatus { pw.print(", original latest="); pw.print(", original latest="); formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); formatRunTime(pw, mOriginalLatestRunTimeElapsedMillis, NO_LATEST_RUNTIME, nowElapsed); pw.println(); pw.println(); if (mCumulativeExecutionTimeMs != 0) { pw.print("Cumulative execution time="); TimeUtils.formatDuration(mCumulativeExecutionTimeMs, pw); pw.println(); } if (numFailures != 0) { if (numFailures != 0) { pw.print("Num failures: "); pw.println(numFailures); pw.print("Num failures: "); pw.println(numFailures); } } Loading
services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java +44 −0 Original line number Original line Diff line number Diff line Loading @@ -337,6 +337,50 @@ public class JobSchedulerServiceTest { mService.getMaxJobExecutionTimeMs(jobUIDT)); mService.getMaxJobExecutionTimeMs(jobUIDT)); } } /** * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * returns a job that is no longer allowed to run as a user-initiated job after it hits * the cumulative execution limit. */ @Test public void testGetRescheduleJobForFailure_cumulativeExecution() { JobStatus originalJob = createJobStatus("testGetRescheduleJobForFailure", createJobInfo() .setUserInitiated(true) .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)); assertTrue(originalJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 0 JobStatus rescheduledJob = mService.getRescheduleJobForFailureLocked(originalJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 50% of limit rescheduledJob.incrementCumulativeExecutionTime( mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 99.999999% of limit rescheduledJob.incrementCumulativeExecutionTime( mService.mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS / 2 - 1); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertTrue(rescheduledJob.shouldTreatAsUserInitiatedJob()); // Cumulative time = 100+% of limit rescheduledJob.incrementCumulativeExecutionTime(2); rescheduledJob = mService.getRescheduleJobForFailureLocked(rescheduledJob, JobParameters.STOP_REASON_UNDEFINED, JobParameters.INTERNAL_STOP_REASON_UNKNOWN); assertFalse(rescheduledJob.shouldTreatAsUserInitiatedJob()); } /** /** * Confirm that * Confirm that * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} * {@link JobSchedulerService#getRescheduleJobForFailureLocked(JobStatus, int, int)} Loading