Merge "Disallow EJs that time out from running in Doze or battery saver." into sc-dev
This commit is contained in:
@@ -25,7 +25,9 @@ import com.android.server.job.controllers.JobStatus;
|
||||
public interface JobCompletedListener {
|
||||
/**
|
||||
* Callback for when a job is completed.
|
||||
*
|
||||
* @param stopReason The stop reason provided to JobParameters.
|
||||
* @param needsReschedule Whether the implementing class should reschedule this job.
|
||||
*/
|
||||
void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule);
|
||||
void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule);
|
||||
}
|
||||
|
||||
@@ -1745,11 +1745,12 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
* A job just finished executing. We fetch the
|
||||
* {@link com.android.server.job.controllers.JobStatus} from the store and depending on
|
||||
* whether we want to reschedule we re-add it to the controllers.
|
||||
* @param jobStatus Completed job.
|
||||
*
|
||||
* @param jobStatus Completed job.
|
||||
* @param needsReschedule Whether the implementing class should reschedule this job.
|
||||
*/
|
||||
@Override
|
||||
public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
|
||||
public void onJobCompletedLocked(JobStatus jobStatus, int stopReason, boolean needsReschedule) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
|
||||
}
|
||||
@@ -1767,6 +1768,11 @@ public class JobSchedulerService extends com.android.server.SystemService
|
||||
// we stop it.
|
||||
final JobStatus rescheduledJob = needsReschedule
|
||||
? getRescheduleJobForFailureLocked(jobStatus) : null;
|
||||
if (rescheduledJob != null
|
||||
&& (stopReason == JobParameters.REASON_TIMEOUT
|
||||
|| stopReason == JobParameters.REASON_PREEMPT)) {
|
||||
rescheduledJob.disallowRunInBatterySaverAndDoze();
|
||||
}
|
||||
|
||||
// Do not write back immediately if this is a periodic job. The job may get lost if system
|
||||
// shuts down before it is added back.
|
||||
|
||||
@@ -381,8 +381,8 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
}
|
||||
|
||||
boolean isWithinExecutionGuaranteeTime() {
|
||||
return mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis
|
||||
< sElapsedRealtimeClock.millis();
|
||||
return sElapsedRealtimeClock.millis()
|
||||
< mExecutionStartTimeElapsed + mMinExecutionGuaranteeMillis;
|
||||
}
|
||||
|
||||
@GuardedBy("mLock")
|
||||
@@ -850,11 +850,12 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
}
|
||||
applyStoppedReasonLocked(reason);
|
||||
completedJob = mRunningJob;
|
||||
mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason(), reason);
|
||||
final int stopReason = mParams.getStopReason();
|
||||
mJobPackageTracker.noteInactive(completedJob, stopReason, reason);
|
||||
FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
|
||||
completedJob.getSourceUid(), null, completedJob.getBatteryName(),
|
||||
FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__FINISHED,
|
||||
mParams.getStopReason(), completedJob.getStandbyBucket(), completedJob.getJobId(),
|
||||
stopReason, completedJob.getStandbyBucket(), completedJob.getJobId(),
|
||||
completedJob.hasChargingConstraint(),
|
||||
completedJob.hasBatteryNotLowConstraint(),
|
||||
completedJob.hasStorageNotLowConstraint(),
|
||||
@@ -865,7 +866,7 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
completedJob.hasContentTriggerConstraint());
|
||||
try {
|
||||
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
|
||||
mParams.getStopReason());
|
||||
stopReason);
|
||||
} catch (RemoteException e) {
|
||||
// Whatever.
|
||||
}
|
||||
@@ -884,7 +885,7 @@ public final class JobServiceContext implements ServiceConnection {
|
||||
service = null;
|
||||
mAvailable = true;
|
||||
removeOpTimeOutLocked();
|
||||
mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
|
||||
mCompletedListener.onJobCompletedLocked(completedJob, stopReason, reschedule);
|
||||
mJobConcurrencyManager.onJobCompletedLocked(this, completedJob, workType);
|
||||
}
|
||||
|
||||
|
||||
@@ -954,7 +954,7 @@ public final class JobStore {
|
||||
appBucket, sourceTag,
|
||||
elapsedRuntimes.first, elapsedRuntimes.second,
|
||||
lastSuccessfulRunTime, lastFailedRunTime,
|
||||
(rtcIsGood) ? null : rtcRuntimes, internalFlags);
|
||||
(rtcIsGood) ? null : rtcRuntimes, internalFlags, /* dynamicConstraints */ 0);
|
||||
return js;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public final class DeviceIdleJobsController extends StateController {
|
||||
* when the app is temp whitelisted or in the foreground.
|
||||
*/
|
||||
private final ArraySet<JobStatus> mAllowInIdleJobs;
|
||||
private final SparseBooleanArray mForegroundUids;
|
||||
private final SparseBooleanArray mForegroundUids = new SparseBooleanArray();
|
||||
private final DeviceIdleUpdateFunctor mDeviceIdleUpdateFunctor;
|
||||
private final DeviceIdleJobsDelayHandler mHandler;
|
||||
private final PowerManager mPowerManager;
|
||||
@@ -77,7 +77,6 @@ public final class DeviceIdleJobsController extends StateController {
|
||||
private int[] mDeviceIdleWhitelistAppIds;
|
||||
private int[] mPowerSaveTempWhitelistAppIds;
|
||||
|
||||
// onReceive
|
||||
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
@@ -120,6 +119,10 @@ public final class DeviceIdleJobsController extends StateController {
|
||||
}
|
||||
};
|
||||
|
||||
/** Criteria for whether or not we should a job's rush evaluation when the device exits Doze. */
|
||||
private final Predicate<JobStatus> mShouldRushEvaluation = (jobStatus) ->
|
||||
jobStatus.isRequestedExpeditedJob() || mForegroundUids.get(jobStatus.getSourceUid());
|
||||
|
||||
public DeviceIdleJobsController(JobSchedulerService service) {
|
||||
super(service);
|
||||
|
||||
@@ -133,7 +136,6 @@ public final class DeviceIdleJobsController extends StateController {
|
||||
mLocalDeviceIdleController.getPowerSaveTempWhitelistAppIds();
|
||||
mDeviceIdleUpdateFunctor = new DeviceIdleUpdateFunctor();
|
||||
mAllowInIdleJobs = new ArraySet<>();
|
||||
mForegroundUids = new SparseBooleanArray();
|
||||
final IntentFilter filter = new IntentFilter();
|
||||
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
|
||||
filter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
|
||||
@@ -156,14 +158,9 @@ public final class DeviceIdleJobsController extends StateController {
|
||||
mHandler.removeMessages(PROCESS_BACKGROUND_JOBS);
|
||||
mService.getJobStore().forEachJob(mDeviceIdleUpdateFunctor);
|
||||
} else {
|
||||
// When coming out of doze, process all foreground uids immediately, while others
|
||||
// will be processed after a delay of 3 seconds.
|
||||
for (int i = 0; i < mForegroundUids.size(); i++) {
|
||||
if (mForegroundUids.valueAt(i)) {
|
||||
mService.getJobStore().forEachJobForSourceUid(
|
||||
mForegroundUids.keyAt(i), mDeviceIdleUpdateFunctor);
|
||||
}
|
||||
}
|
||||
// When coming out of doze, process all foreground uids and EJs immediately,
|
||||
// while others will be processed after a delay of 3 seconds.
|
||||
mService.getJobStore().forEachJob(mShouldRushEvaluation, mDeviceIdleUpdateFunctor);
|
||||
mHandler.sendEmptyMessageDelayed(PROCESS_BACKGROUND_JOBS, BACKGROUND_JOBS_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,12 @@ public final class JobStatus {
|
||||
static final int CONSTRAINT_WITHIN_EXPEDITED_QUOTA = 1 << 23; // Implicit constraint
|
||||
static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1 << 22; // Implicit constraint
|
||||
|
||||
// The following set of dynamic constraints are for specific use cases (as explained in their
|
||||
// relative naming and comments). Right now, they apply different constraints, which is fine,
|
||||
// but if in the future, we have overlapping dynamic constraint sets, removing one constraint
|
||||
// set may accidentally remove a constraint applied by another dynamic set.
|
||||
// TODO: properly handle overlapping dynamic constraint sets
|
||||
|
||||
/**
|
||||
* The additional set of dynamic constraints that must be met if the job's effective bucket is
|
||||
* {@link JobSchedulerService#RESTRICTED_INDEX}. Connectivity can be ignored if the job doesn't
|
||||
@@ -102,6 +108,13 @@ public final class JobStatus {
|
||||
| CONSTRAINT_CONNECTIVITY
|
||||
| CONSTRAINT_IDLE;
|
||||
|
||||
/**
|
||||
* The additional set of dynamic constraints that must be met if this is an expedited job that
|
||||
* had a long enough run while the device was Dozing or in battery saver.
|
||||
*/
|
||||
private static final int DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS =
|
||||
CONSTRAINT_DEVICE_NOT_DOZING | CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
|
||||
|
||||
/**
|
||||
* Standard media URIs that contain the media files that might be important to the user.
|
||||
* @see #mHasMediaBackupExemption
|
||||
@@ -426,7 +439,8 @@ public final class JobStatus {
|
||||
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
|
||||
int sourceUserId, int standbyBucket, String tag, int numFailures,
|
||||
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
|
||||
long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags) {
|
||||
long lastSuccessfulRunTime, long lastFailedRunTime, int internalFlags,
|
||||
int dynamicConstraints) {
|
||||
this.job = job;
|
||||
this.callingUid = callingUid;
|
||||
this.standbyBucket = standbyBucket;
|
||||
@@ -487,6 +501,7 @@ public final class JobStatus {
|
||||
}
|
||||
this.requiredConstraints = requiredConstraints;
|
||||
mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
|
||||
addDynamicConstraints(dynamicConstraints);
|
||||
mReadyNotDozing = canRunInDoze();
|
||||
if (standbyBucket == RESTRICTED_INDEX) {
|
||||
addDynamicConstraints(DYNAMIC_RESTRICTED_CONSTRAINTS);
|
||||
@@ -521,7 +536,7 @@ public final class JobStatus {
|
||||
jobStatus.getSourceTag(), jobStatus.getNumFailures(),
|
||||
jobStatus.getEarliestRunTime(), jobStatus.getLatestRunTimeElapsed(),
|
||||
jobStatus.getLastSuccessfulRunTime(), jobStatus.getLastFailedRunTime(),
|
||||
jobStatus.getInternalFlags());
|
||||
jobStatus.getInternalFlags(), jobStatus.mDynamicConstraints);
|
||||
mPersistedUtcTimes = jobStatus.mPersistedUtcTimes;
|
||||
if (jobStatus.mPersistedUtcTimes != null) {
|
||||
if (DEBUG) {
|
||||
@@ -543,12 +558,12 @@ public final class JobStatus {
|
||||
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis,
|
||||
long lastSuccessfulRunTime, long lastFailedRunTime,
|
||||
Pair<Long, Long> persistedExecutionTimesUTC,
|
||||
int innerFlags) {
|
||||
int innerFlags, int dynamicConstraints) {
|
||||
this(job, callingUid, sourcePkgName, sourceUserId,
|
||||
standbyBucket,
|
||||
sourceTag, 0,
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
|
||||
lastSuccessfulRunTime, lastFailedRunTime, innerFlags);
|
||||
lastSuccessfulRunTime, lastFailedRunTime, innerFlags, dynamicConstraints);
|
||||
|
||||
// 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
|
||||
@@ -572,7 +587,8 @@ public final class JobStatus {
|
||||
rescheduling.getStandbyBucket(),
|
||||
rescheduling.getSourceTag(), backoffAttempt, newEarliestRuntimeElapsedMillis,
|
||||
newLatestRuntimeElapsedMillis,
|
||||
lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags());
|
||||
lastSuccessfulRunTime, lastFailedRunTime, rescheduling.getInternalFlags(),
|
||||
rescheduling.mDynamicConstraints);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -609,7 +625,7 @@ public final class JobStatus {
|
||||
standbyBucket, tag, 0,
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
|
||||
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
|
||||
/*innerFlags=*/ 0);
|
||||
/*innerFlags=*/ 0, /* dynamicConstraints */ 0);
|
||||
}
|
||||
|
||||
public void enqueueWorkLocked(JobWorkItem work) {
|
||||
@@ -1083,12 +1099,15 @@ public final class JobStatus {
|
||||
* in Doze.
|
||||
*/
|
||||
public boolean canRunInDoze() {
|
||||
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0 || shouldTreatAsExpeditedJob();
|
||||
return (getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0
|
||||
|| (shouldTreatAsExpeditedJob()
|
||||
&& (mDynamicConstraints & CONSTRAINT_DEVICE_NOT_DOZING) == 0);
|
||||
}
|
||||
|
||||
boolean canRunInBatterySaver() {
|
||||
return (getInternalFlags() & INTERNAL_FLAG_HAS_FOREGROUND_EXEMPTION) != 0
|
||||
|| shouldTreatAsExpeditedJob();
|
||||
|| (shouldTreatAsExpeditedJob()
|
||||
&& (mDynamicConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) == 0);
|
||||
}
|
||||
|
||||
boolean shouldIgnoreNetworkBlocking() {
|
||||
@@ -1244,6 +1263,14 @@ public final class JobStatus {
|
||||
trackingControllers |= which;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add additional constraints to prevent this job from running when doze or battery saver are
|
||||
* active.
|
||||
*/
|
||||
public void disallowRunInBatterySaverAndDoze() {
|
||||
addDynamicConstraints(DYNAMIC_EXPEDITED_DEFERRAL_CONSTRAINTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates that this job cannot run without the specified constraints. This is evaluated
|
||||
* separately from the job's explicitly requested constraints and MUST be satisfied before
|
||||
|
||||
@@ -641,6 +641,6 @@ public class ConnectivityControllerTest {
|
||||
private static JobStatus createJobStatus(JobInfo.Builder job, int uid,
|
||||
long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
|
||||
return new JobStatus(job.build(), uid, null, -1, 0, null,
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0);
|
||||
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,7 +685,7 @@ public class JobStatusTest {
|
||||
final JobInfo job = new JobInfo.Builder(101, new ComponentName("foo", "bar"))
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).build();
|
||||
return new JobStatus(job, 0, null, -1, 0, null, earliestRunTimeElapsedMillis,
|
||||
latestRunTimeElapsedMillis, 0, 0, null, 0);
|
||||
latestRunTimeElapsedMillis, 0, 0, null, 0, 0);
|
||||
}
|
||||
|
||||
private static JobStatus createJobStatus(JobInfo job) {
|
||||
|
||||
@@ -276,7 +276,7 @@ public class JobStoreTest {
|
||||
0 /* sourceUserId */, 0, "someTag",
|
||||
invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis,
|
||||
0 /* lastSuccessfulRunTime */, 0 /* lastFailedRunTime */,
|
||||
persistedExecutionTimesUTC, 0 /* innerFlagg */);
|
||||
persistedExecutionTimesUTC, 0 /* innerFlag */, 0 /* dynamicConstraints */);
|
||||
|
||||
mTaskStoreUnderTest.add(js);
|
||||
waitForPendingIo();
|
||||
|
||||
Reference in New Issue
Block a user