Merge "Fix issue #32180780: Sync adapters inappropriately being run..." into oc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
0fa2574ecb
@@ -244,7 +244,7 @@ public class JobInfo implements Parcelable {
|
||||
/**
|
||||
* Bundle of extras which are returned to your application at execution time.
|
||||
*/
|
||||
public PersistableBundle getExtras() {
|
||||
public @NonNull PersistableBundle getExtras() {
|
||||
return extras;
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ public class JobInfo implements Parcelable {
|
||||
* Bundle of transient extras which are returned to your application at execution time,
|
||||
* but not persisted by the system.
|
||||
*/
|
||||
public Bundle getTransientExtras() {
|
||||
public @NonNull Bundle getTransientExtras() {
|
||||
return transientExtras;
|
||||
}
|
||||
|
||||
@@ -260,7 +260,7 @@ public class JobInfo implements Parcelable {
|
||||
* ClipData of information that is returned to your application at execution time,
|
||||
* but not persisted by the system.
|
||||
*/
|
||||
public ClipData getClipData() {
|
||||
public @Nullable ClipData getClipData() {
|
||||
return clipData;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,7 @@ public class JobInfo implements Parcelable {
|
||||
/**
|
||||
* Name of the service endpoint that will be called back into by the JobScheduler.
|
||||
*/
|
||||
public ComponentName getService() {
|
||||
public @NonNull ComponentName getService() {
|
||||
return service;
|
||||
}
|
||||
|
||||
@@ -327,8 +327,7 @@ public class JobInfo implements Parcelable {
|
||||
* Which content: URIs must change for the job to be scheduled. Returns null
|
||||
* if there are none required.
|
||||
*/
|
||||
@Nullable
|
||||
public TriggerContentUri[] getTriggerContentUris() {
|
||||
public @Nullable TriggerContentUri[] getTriggerContentUris() {
|
||||
return triggerContentUris;
|
||||
}
|
||||
|
||||
@@ -811,7 +810,7 @@ public class JobInfo implements Parcelable {
|
||||
* @param jobService The endpoint that you implement that will receive the callback from the
|
||||
* JobScheduler.
|
||||
*/
|
||||
public Builder(int jobId, ComponentName jobService) {
|
||||
public Builder(int jobId, @NonNull ComponentName jobService) {
|
||||
mJobService = jobService;
|
||||
mJobId = jobId;
|
||||
}
|
||||
@@ -832,7 +831,7 @@ public class JobInfo implements Parcelable {
|
||||
* Set optional extras. This is persisted, so we only allow primitive types.
|
||||
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
|
||||
*/
|
||||
public Builder setExtras(PersistableBundle extras) {
|
||||
public Builder setExtras(@NonNull PersistableBundle extras) {
|
||||
mExtras = extras;
|
||||
return this;
|
||||
}
|
||||
@@ -842,7 +841,7 @@ public class JobInfo implements Parcelable {
|
||||
* persisted with {@link #setPersisted(boolean)}; mixing the two is not allowed.
|
||||
* @param extras Bundle containing extras you want the scheduler to hold on to for you.
|
||||
*/
|
||||
public Builder setTransientExtras(Bundle extras) {
|
||||
public Builder setTransientExtras(@NonNull Bundle extras) {
|
||||
mTransientExtras = extras;
|
||||
return this;
|
||||
}
|
||||
@@ -869,7 +868,7 @@ public class JobInfo implements Parcelable {
|
||||
* {@link android.content.Intent#FLAG_GRANT_WRITE_URI_PERMISSION}, and
|
||||
* {@link android.content.Intent#FLAG_GRANT_PREFIX_URI_PERMISSION}.
|
||||
*/
|
||||
public Builder setClipData(ClipData clip, int grantFlags) {
|
||||
public Builder setClipData(@Nullable ClipData clip, int grantFlags) {
|
||||
mClipData = clip;
|
||||
mClipGrantFlags = grantFlags;
|
||||
return this;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.app.job;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.job.IJobCallback;
|
||||
import android.content.ClipData;
|
||||
import android.net.Uri;
|
||||
@@ -91,7 +93,7 @@ public class JobParameters implements Parcelable {
|
||||
* {@link android.app.job.JobInfo.Builder#setExtras(android.os.PersistableBundle)}. This will
|
||||
* never be null. If you did not set any extras this will be an empty bundle.
|
||||
*/
|
||||
public PersistableBundle getExtras() {
|
||||
public @NonNull PersistableBundle getExtras() {
|
||||
return extras;
|
||||
}
|
||||
|
||||
@@ -100,7 +102,7 @@ public class JobParameters implements Parcelable {
|
||||
* {@link android.app.job.JobInfo.Builder#setTransientExtras(android.os.Bundle)}. This will
|
||||
* never be null. If you did not set any extras this will be an empty bundle.
|
||||
*/
|
||||
public Bundle getTransientExtras() {
|
||||
public @NonNull Bundle getTransientExtras() {
|
||||
return transientExtras;
|
||||
}
|
||||
|
||||
@@ -109,7 +111,7 @@ public class JobParameters implements Parcelable {
|
||||
* {@link android.app.job.JobInfo.Builder#setClipData(ClipData, int)}. Will be null
|
||||
* if it was not set.
|
||||
*/
|
||||
public ClipData getClipData() {
|
||||
public @Nullable ClipData getClipData() {
|
||||
return clipData;
|
||||
}
|
||||
|
||||
@@ -140,7 +142,7 @@ public class JobParameters implements Parcelable {
|
||||
* always use {@link #getTriggeredContentAuthorities()} to determine whether the job was
|
||||
* triggered due to any content changes and the authorities they are associated with.
|
||||
*/
|
||||
public Uri[] getTriggeredContentUris() {
|
||||
public @Nullable Uri[] getTriggeredContentUris() {
|
||||
return mTriggeredContentUris;
|
||||
}
|
||||
|
||||
@@ -152,7 +154,7 @@ public class JobParameters implements Parcelable {
|
||||
* to retrieve the details of which URIs changed (as long as that has not exceeded the maximum
|
||||
* number it can reported).
|
||||
*/
|
||||
public String[] getTriggeredContentAuthorities() {
|
||||
public @Nullable String[] getTriggeredContentAuthorities() {
|
||||
return mTriggeredContentAuthorities;
|
||||
}
|
||||
|
||||
@@ -183,7 +185,7 @@ public class JobParameters implements Parcelable {
|
||||
* (This means that for correct operation, you must always call dequeueWork() after you have
|
||||
* completed other work, to check either for more work or allow the system to stop the job.)
|
||||
*/
|
||||
public JobWorkItem dequeueWork() {
|
||||
public @Nullable JobWorkItem dequeueWork() {
|
||||
try {
|
||||
return getCallback().dequeueWork(getJobId());
|
||||
} catch (RemoteException e) {
|
||||
@@ -207,7 +209,7 @@ public class JobParameters implements Parcelable {
|
||||
* @param work The work you have completed processing, as previously returned by
|
||||
* {@link #dequeueWork()}
|
||||
*/
|
||||
public void completeWork(JobWorkItem work) {
|
||||
public void completeWork(@NonNull JobWorkItem work) {
|
||||
try {
|
||||
if (!getCallback().completeWork(getJobId(), work.getWorkId())) {
|
||||
throw new IllegalArgumentException("Given work is not active: " + work);
|
||||
|
||||
@@ -72,7 +72,7 @@ public abstract class JobScheduler {
|
||||
* you can schedule.
|
||||
* @return An int representing ({@link #RESULT_SUCCESS} or {@link #RESULT_FAILURE}).
|
||||
*/
|
||||
public abstract int schedule(JobInfo job);
|
||||
public abstract int schedule(@NonNull JobInfo job);
|
||||
|
||||
/**
|
||||
* Similar to {@link #schedule}, but allows you to enqueue work for an existing job. If a job
|
||||
@@ -108,7 +108,7 @@ public abstract class JobScheduler {
|
||||
* @param work New work to enqueue. This will be available later when the job starts running.
|
||||
* @return An int representing ({@link #RESULT_SUCCESS} or {@link #RESULT_FAILURE}).
|
||||
*/
|
||||
public abstract int enqueue(JobInfo job, JobWorkItem work);
|
||||
public abstract int enqueue(@NonNull JobInfo job, @NonNull JobWorkItem work);
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -121,7 +121,8 @@ public abstract class JobScheduler {
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public abstract int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag);
|
||||
public abstract int scheduleAsPackage(@NonNull JobInfo job, @NonNull String packageName,
|
||||
int userId, String tag);
|
||||
|
||||
/**
|
||||
* Cancel a job that is pending in the JobScheduler.
|
||||
|
||||
@@ -358,6 +358,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import com.android.server.job.JobSchedulerInternal;
|
||||
import com.google.android.collect.Lists;
|
||||
import com.google.android.collect.Maps;
|
||||
|
||||
@@ -18179,6 +18180,9 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return false;
|
||||
}
|
||||
|
||||
int oldBackupUid;
|
||||
int newBackupUid;
|
||||
|
||||
synchronized(this) {
|
||||
// !!! TODO: currently no check here that we're already bound
|
||||
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
|
||||
@@ -18219,6 +18223,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
proc.inFullBackup = true;
|
||||
}
|
||||
r.app = proc;
|
||||
oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
|
||||
newBackupUid = proc.inFullBackup ? r.appInfo.uid : -1;
|
||||
mBackupTarget = r;
|
||||
mBackupAppName = app.packageName;
|
||||
|
||||
@@ -18244,6 +18250,14 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
// know that it's scheduled for a backup-agent operation.
|
||||
}
|
||||
|
||||
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
|
||||
if (oldBackupUid != -1) {
|
||||
js.removeBackingUpUid(oldBackupUid);
|
||||
}
|
||||
if (newBackupUid != -1) {
|
||||
js.addBackingUpUid(newBackupUid);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -18256,6 +18270,9 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mBackupTarget = null;
|
||||
mBackupAppName = null;
|
||||
}
|
||||
|
||||
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
|
||||
js.clearAllBackingUpUids();
|
||||
}
|
||||
|
||||
// A backup agent has just come up
|
||||
@@ -18293,6 +18310,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
int oldBackupUid;
|
||||
|
||||
synchronized(this) {
|
||||
try {
|
||||
if (mBackupAppName == null) {
|
||||
@@ -18310,6 +18329,8 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
updateOomAdjLocked(proc);
|
||||
proc.inFullBackup = false;
|
||||
|
||||
oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
|
||||
|
||||
// If the app crashed during backup, 'thread' will be null here
|
||||
if (proc.thread != null) {
|
||||
try {
|
||||
@@ -18325,7 +18346,13 @@ public class ActivityManagerService extends IActivityManager.Stub
|
||||
mBackupAppName = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (oldBackupUid != -1) {
|
||||
JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
|
||||
js.removeBackingUpUid(oldBackupUid);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================
|
||||
// BROADCASTS
|
||||
// =========================================================
|
||||
|
||||
@@ -30,4 +30,11 @@ public interface JobSchedulerInternal {
|
||||
* Returns a list of pending jobs scheduled by the system service.
|
||||
*/
|
||||
List<JobInfo> getSystemScheduledPendingJobs();
|
||||
|
||||
/**
|
||||
* These are for activity manager to communicate to use what is currently performing backups.
|
||||
*/
|
||||
void addBackingUpUid(int uid);
|
||||
void removeBackingUpUid(int uid);
|
||||
void clearAllBackingUpUids();
|
||||
}
|
||||
|
||||
@@ -174,6 +174,11 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
*/
|
||||
final SparseIntArray mUidPriorityOverride = new SparseIntArray();
|
||||
|
||||
/**
|
||||
* Which uids are currently performing backups, so we shouldn't allow their jobs to run.
|
||||
*/
|
||||
final SparseIntArray mBackingUpUids = new SparseIntArray();
|
||||
|
||||
// -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
|
||||
|
||||
/**
|
||||
@@ -621,14 +626,30 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
jobStatus.prepareLocked(ActivityManager.getService());
|
||||
|
||||
if (toCancel != null) {
|
||||
cancelJobImpl(toCancel, jobStatus);
|
||||
cancelJobImplLocked(toCancel, jobStatus);
|
||||
}
|
||||
if (work != null) {
|
||||
// If work has been supplied, enqueue it into the new job.
|
||||
jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
|
||||
}
|
||||
startTrackingJobLocked(jobStatus, toCancel);
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
|
||||
// If the job is immediately ready to run, then we can just immediately
|
||||
// put it in the pending list and try to schedule it. This is especially
|
||||
// important for jobs with a 0 deadline constraint, since they will happen a fair
|
||||
// amount, we want to handle them as quickly as possible, and semantically we want to
|
||||
// make sure we have started holding the wake lock for the job before returning to
|
||||
// the caller.
|
||||
// If the job is not yet ready to run, there is nothing more to do -- we are
|
||||
// now just waiting for one of its controllers to change state and schedule
|
||||
// the job appropriately.
|
||||
if (isReadyToBeExecutedLocked(jobStatus)) {
|
||||
// This is a new job, we can just immediately put it on the pending
|
||||
// list and try to run it.
|
||||
mJobPackageTracker.notePending(jobStatus);
|
||||
mPendingJobs.add(jobStatus);
|
||||
maybeRunPendingJobsLocked();
|
||||
}
|
||||
}
|
||||
return JobScheduler.RESULT_SUCCESS;
|
||||
}
|
||||
@@ -659,25 +680,23 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
}
|
||||
|
||||
void cancelJobsForUser(int userHandle) {
|
||||
List<JobStatus> jobsForUser;
|
||||
synchronized (mLock) {
|
||||
jobsForUser = mJobs.getJobsByUser(userHandle);
|
||||
}
|
||||
for (int i=0; i<jobsForUser.size(); i++) {
|
||||
JobStatus toRemove = jobsForUser.get(i);
|
||||
cancelJobImpl(toRemove, null);
|
||||
final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
|
||||
for (int i=0; i<jobsForUser.size(); i++) {
|
||||
JobStatus toRemove = jobsForUser.get(i);
|
||||
cancelJobImplLocked(toRemove, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cancelJobsForPackageAndUid(String pkgName, int uid) {
|
||||
List<JobStatus> jobsForUid;
|
||||
synchronized (mLock) {
|
||||
jobsForUid = mJobs.getJobsByUid(uid);
|
||||
}
|
||||
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
|
||||
final JobStatus job = jobsForUid.get(i);
|
||||
if (job.getSourcePackageName().equals(pkgName)) {
|
||||
cancelJobImpl(job, null);
|
||||
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
|
||||
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
|
||||
final JobStatus job = jobsForUid.get(i);
|
||||
if (job.getSourcePackageName().equals(pkgName)) {
|
||||
cancelJobImplLocked(job, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -690,13 +709,12 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
*
|
||||
*/
|
||||
public void cancelJobsForUid(int uid) {
|
||||
List<JobStatus> jobsForUid;
|
||||
synchronized (mLock) {
|
||||
jobsForUid = mJobs.getJobsByUid(uid);
|
||||
}
|
||||
for (int i=0; i<jobsForUid.size(); i++) {
|
||||
JobStatus toRemove = jobsForUid.get(i);
|
||||
cancelJobImpl(toRemove, null);
|
||||
final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
|
||||
for (int i=0; i<jobsForUid.size(); i++) {
|
||||
JobStatus toRemove = jobsForUid.get(i);
|
||||
cancelJobImplLocked(toRemove, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,25 +729,23 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
JobStatus toCancel;
|
||||
synchronized (mLock) {
|
||||
toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
|
||||
}
|
||||
if (toCancel != null) {
|
||||
cancelJobImpl(toCancel, null);
|
||||
if (toCancel != null) {
|
||||
cancelJobImplLocked(toCancel, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
|
||||
synchronized (mLock) {
|
||||
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
|
||||
cancelled.unprepareLocked(ActivityManager.getService());
|
||||
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
|
||||
// Remove from pending queue.
|
||||
if (mPendingJobs.remove(cancelled)) {
|
||||
mJobPackageTracker.noteNonpending(cancelled);
|
||||
}
|
||||
// Cancel if running.
|
||||
stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
|
||||
reportActiveLocked();
|
||||
private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob) {
|
||||
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
|
||||
cancelled.unprepareLocked(ActivityManager.getService());
|
||||
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
|
||||
// Remove from pending queue.
|
||||
if (mPendingJobs.remove(cancelled)) {
|
||||
mJobPackageTracker.noteNonpending(cancelled);
|
||||
}
|
||||
// Cancel if running.
|
||||
stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
|
||||
reportActiveLocked();
|
||||
}
|
||||
|
||||
void updateUidState(int uid, int procState) {
|
||||
@@ -770,8 +786,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
mLocalDeviceIdleController.setJobsActive(true);
|
||||
}
|
||||
}
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
}
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -990,7 +1006,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
* @return A newly instantiated JobStatus with the same constraints as the last job except
|
||||
* with adjusted timing constraints.
|
||||
*
|
||||
* @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
|
||||
* @see #maybeQueueReadyJobsForExecutionLocked
|
||||
*/
|
||||
private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
|
||||
final long elapsedNowMillis = SystemClock.elapsedRealtime();
|
||||
@@ -1128,7 +1144,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
|
||||
}
|
||||
|
||||
private class JobHandler extends Handler {
|
||||
final private class JobHandler extends Handler {
|
||||
|
||||
public JobHandler(Looper looper) {
|
||||
super(looper);
|
||||
@@ -1140,283 +1156,278 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
if (!mReadyToRock) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
switch (message.what) {
|
||||
case MSG_JOB_EXPIRED:
|
||||
synchronized (mLock) {
|
||||
switch (message.what) {
|
||||
case MSG_JOB_EXPIRED: {
|
||||
JobStatus runNow = (JobStatus) message.obj;
|
||||
// runNow can be null, which is a controller's way of indicating that its
|
||||
// state is such that all ready jobs should be run immediately.
|
||||
if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
|
||||
mJobPackageTracker.notePending(runNow);
|
||||
mPendingJobs.add(runNow);
|
||||
} else {
|
||||
queueReadyJobsForExecutionLocked();
|
||||
}
|
||||
queueReadyJobsForExecutionLockedH();
|
||||
}
|
||||
break;
|
||||
case MSG_CHECK_JOB:
|
||||
synchronized (mLock) {
|
||||
} break;
|
||||
case MSG_CHECK_JOB:
|
||||
if (mReportedActive) {
|
||||
// if jobs are currently being run, queue all ready jobs for execution.
|
||||
queueReadyJobsForExecutionLockedH();
|
||||
queueReadyJobsForExecutionLocked();
|
||||
} else {
|
||||
// Check the list of jobs and run some of them if we feel inclined.
|
||||
maybeQueueReadyJobsForExecutionLockedH();
|
||||
maybeQueueReadyJobsForExecutionLocked();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_CHECK_JOB_GREEDY:
|
||||
synchronized (mLock) {
|
||||
queueReadyJobsForExecutionLockedH();
|
||||
}
|
||||
break;
|
||||
case MSG_STOP_JOB:
|
||||
cancelJobImpl((JobStatus)message.obj, null);
|
||||
break;
|
||||
}
|
||||
maybeRunPendingJobsH();
|
||||
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
|
||||
removeMessages(MSG_CHECK_JOB);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run through list of jobs and execute all possible - at least one is expired so we do
|
||||
* as many as we can.
|
||||
*/
|
||||
private void queueReadyJobsForExecutionLockedH() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "queuing all ready jobs for execution:");
|
||||
}
|
||||
noteJobsNonpending(mPendingJobs);
|
||||
mPendingJobs.clear();
|
||||
mJobs.forEachJob(mReadyQueueFunctor);
|
||||
mReadyQueueFunctor.postProcess();
|
||||
|
||||
if (DEBUG) {
|
||||
final int queuedJobs = mPendingJobs.size();
|
||||
if (queuedJobs == 0) {
|
||||
Slog.d(TAG, "No jobs pending.");
|
||||
} else {
|
||||
Slog.d(TAG, queuedJobs + " jobs queued.");
|
||||
break;
|
||||
case MSG_CHECK_JOB_GREEDY:
|
||||
queueReadyJobsForExecutionLocked();
|
||||
break;
|
||||
case MSG_STOP_JOB:
|
||||
cancelJobImplLocked((JobStatus) message.obj, null);
|
||||
break;
|
||||
}
|
||||
maybeRunPendingJobsLocked();
|
||||
// Don't remove JOB_EXPIRED in case one came along while processing the queue.
|
||||
removeMessages(MSG_CHECK_JOB);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ReadyJobQueueFunctor implements JobStatusFunctor {
|
||||
ArrayList<JobStatus> newReadyJobs;
|
||||
/**
|
||||
* Run through list of jobs and execute all possible - at least one is expired so we do
|
||||
* as many as we can.
|
||||
*/
|
||||
private void queueReadyJobsForExecutionLocked() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "queuing all ready jobs for execution:");
|
||||
}
|
||||
noteJobsNonpending(mPendingJobs);
|
||||
mPendingJobs.clear();
|
||||
mJobs.forEachJob(mReadyQueueFunctor);
|
||||
mReadyQueueFunctor.postProcess();
|
||||
|
||||
@Override
|
||||
public void process(JobStatus job) {
|
||||
if (isReadyToBeExecutedLocked(job)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, " queued " + job.toShortString());
|
||||
}
|
||||
if (newReadyJobs == null) {
|
||||
newReadyJobs = new ArrayList<JobStatus>();
|
||||
}
|
||||
newReadyJobs.add(job);
|
||||
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
|
||||
stopJobOnServiceContextLocked(job,
|
||||
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
public void postProcess() {
|
||||
if (newReadyJobs != null) {
|
||||
noteJobsPending(newReadyJobs);
|
||||
mPendingJobs.addAll(newReadyJobs);
|
||||
}
|
||||
newReadyJobs = null;
|
||||
if (DEBUG) {
|
||||
final int queuedJobs = mPendingJobs.size();
|
||||
if (queuedJobs == 0) {
|
||||
Slog.d(TAG, "No jobs pending.");
|
||||
} else {
|
||||
Slog.d(TAG, queuedJobs + " jobs queued.");
|
||||
}
|
||||
}
|
||||
private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
|
||||
}
|
||||
|
||||
/**
|
||||
* The state of at least one job has changed. Here is where we could enforce various
|
||||
* policies on when we want to execute jobs.
|
||||
* Right now the policy is such:
|
||||
* If >1 of the ready jobs is idle mode we send all of them off
|
||||
* if more than 2 network connectivity jobs are ready we send them all off.
|
||||
* If more than 4 jobs total are ready we send them all off.
|
||||
* TODO: It would be nice to consolidate these sort of high-level policies somewhere.
|
||||
*/
|
||||
class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
|
||||
int chargingCount;
|
||||
int batteryNotLowCount;
|
||||
int storageNotLowCount;
|
||||
int idleCount;
|
||||
int backoffCount;
|
||||
int connectivityCount;
|
||||
int contentCount;
|
||||
List<JobStatus> runnableJobs;
|
||||
final class ReadyJobQueueFunctor implements JobStatusFunctor {
|
||||
ArrayList<JobStatus> newReadyJobs;
|
||||
|
||||
public MaybeReadyJobQueueFunctor() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Functor method invoked for each job via JobStore.forEachJob()
|
||||
@Override
|
||||
public void process(JobStatus job) {
|
||||
if (isReadyToBeExecutedLocked(job)) {
|
||||
try {
|
||||
if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
|
||||
job.getJob().getService().getPackageName())) {
|
||||
Slog.w(TAG, "Aborting job " + job.getUid() + ":"
|
||||
+ job.getJob().toString() + " -- package not allowed to start");
|
||||
mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
if (job.getNumFailures() > 0) {
|
||||
backoffCount++;
|
||||
}
|
||||
if (job.hasIdleConstraint()) {
|
||||
idleCount++;
|
||||
}
|
||||
if (job.hasConnectivityConstraint()) {
|
||||
connectivityCount++;
|
||||
}
|
||||
if (job.hasChargingConstraint()) {
|
||||
chargingCount++;
|
||||
}
|
||||
if (job.hasBatteryNotLowConstraint()) {
|
||||
batteryNotLowCount++;
|
||||
}
|
||||
if (job.hasStorageNotLowConstraint()) {
|
||||
storageNotLowCount++;
|
||||
}
|
||||
if (job.hasContentTriggerConstraint()) {
|
||||
contentCount++;
|
||||
}
|
||||
if (runnableJobs == null) {
|
||||
runnableJobs = new ArrayList<>();
|
||||
}
|
||||
runnableJobs.add(job);
|
||||
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
|
||||
stopJobOnServiceContextLocked(job,
|
||||
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
public void postProcess() {
|
||||
if (backoffCount > 0 ||
|
||||
idleCount >= mConstants.MIN_IDLE_COUNT ||
|
||||
connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
|
||||
chargingCount >= mConstants.MIN_CHARGING_COUNT ||
|
||||
batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
|
||||
storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
|
||||
contentCount >= mConstants.MIN_CONTENT_COUNT ||
|
||||
(runnableJobs != null
|
||||
&& runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
|
||||
}
|
||||
noteJobsPending(runnableJobs);
|
||||
mPendingJobs.addAll(runnableJobs);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
|
||||
}
|
||||
}
|
||||
|
||||
// Be ready for next time
|
||||
reset();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
chargingCount = 0;
|
||||
idleCount = 0;
|
||||
backoffCount = 0;
|
||||
connectivityCount = 0;
|
||||
batteryNotLowCount = 0;
|
||||
storageNotLowCount = 0;
|
||||
contentCount = 0;
|
||||
runnableJobs = null;
|
||||
}
|
||||
}
|
||||
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
|
||||
|
||||
private void maybeQueueReadyJobsForExecutionLockedH() {
|
||||
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
|
||||
|
||||
noteJobsNonpending(mPendingJobs);
|
||||
mPendingJobs.clear();
|
||||
mJobs.forEachJob(mMaybeQueueFunctor);
|
||||
mMaybeQueueFunctor.postProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Criteria for moving a job into the pending queue:
|
||||
* - It's ready.
|
||||
* - It's not pending.
|
||||
* - It's not already running on a JSC.
|
||||
* - The user that requested the job is running.
|
||||
* - The component is enabled and runnable.
|
||||
*/
|
||||
private boolean isReadyToBeExecutedLocked(JobStatus job) {
|
||||
final boolean jobExists = mJobs.containsJob(job);
|
||||
final boolean jobReady = job.isReady();
|
||||
final boolean jobPending = mPendingJobs.contains(job);
|
||||
final boolean jobActive = isCurrentlyActiveLocked(job);
|
||||
|
||||
final int userId = job.getUserId();
|
||||
final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
|
||||
+ " exists=" + jobExists
|
||||
+ " ready=" + jobReady + " pending=" + jobPending
|
||||
+ " active=" + jobActive + " userStarted=" + userStarted);
|
||||
}
|
||||
|
||||
// Short circuit: don't do the expensive PM check unless we really think
|
||||
// we might need to run this job now.
|
||||
if (!jobExists || !userStarted || !jobReady || jobPending || jobActive) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean componentPresent;
|
||||
try {
|
||||
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
|
||||
job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
|
||||
userId) != null);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
|
||||
+ " componentPresent=" + componentPresent);
|
||||
}
|
||||
|
||||
// Everything else checked out so far, so this is the final yes/no check
|
||||
return componentPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Criteria for cancelling an active job:
|
||||
* - It's not ready
|
||||
* - It's running on a JSC.
|
||||
*/
|
||||
private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
|
||||
return !job.isReady() && isCurrentlyActiveLocked(job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile jobs in the pending queue against available execution contexts.
|
||||
* A controller can force a job into the pending queue even if it's already running, but
|
||||
* here is where we decide whether to actually execute it.
|
||||
*/
|
||||
private void maybeRunPendingJobsH() {
|
||||
synchronized (mLock) {
|
||||
@Override
|
||||
public void process(JobStatus job) {
|
||||
if (isReadyToBeExecutedLocked(job)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
|
||||
Slog.d(TAG, " queued " + job.toShortString());
|
||||
}
|
||||
assignJobsToContextsLocked();
|
||||
reportActiveLocked();
|
||||
if (newReadyJobs == null) {
|
||||
newReadyJobs = new ArrayList<JobStatus>();
|
||||
}
|
||||
newReadyJobs.add(job);
|
||||
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
|
||||
stopJobOnServiceContextLocked(job,
|
||||
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
public void postProcess() {
|
||||
if (newReadyJobs != null) {
|
||||
noteJobsPending(newReadyJobs);
|
||||
mPendingJobs.addAll(newReadyJobs);
|
||||
}
|
||||
newReadyJobs = null;
|
||||
}
|
||||
}
|
||||
private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
|
||||
|
||||
/**
|
||||
* The state of at least one job has changed. Here is where we could enforce various
|
||||
* policies on when we want to execute jobs.
|
||||
* Right now the policy is such:
|
||||
* If >1 of the ready jobs is idle mode we send all of them off
|
||||
* if more than 2 network connectivity jobs are ready we send them all off.
|
||||
* If more than 4 jobs total are ready we send them all off.
|
||||
* TODO: It would be nice to consolidate these sort of high-level policies somewhere.
|
||||
*/
|
||||
final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
|
||||
int chargingCount;
|
||||
int batteryNotLowCount;
|
||||
int storageNotLowCount;
|
||||
int idleCount;
|
||||
int backoffCount;
|
||||
int connectivityCount;
|
||||
int contentCount;
|
||||
List<JobStatus> runnableJobs;
|
||||
|
||||
public MaybeReadyJobQueueFunctor() {
|
||||
reset();
|
||||
}
|
||||
|
||||
// Functor method invoked for each job via JobStore.forEachJob()
|
||||
@Override
|
||||
public void process(JobStatus job) {
|
||||
if (isReadyToBeExecutedLocked(job)) {
|
||||
try {
|
||||
if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
|
||||
job.getJob().getService().getPackageName())) {
|
||||
Slog.w(TAG, "Aborting job " + job.getUid() + ":"
|
||||
+ job.getJob().toString() + " -- package not allowed to start");
|
||||
mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
|
||||
return;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
if (job.getNumFailures() > 0) {
|
||||
backoffCount++;
|
||||
}
|
||||
if (job.hasIdleConstraint()) {
|
||||
idleCount++;
|
||||
}
|
||||
if (job.hasConnectivityConstraint()) {
|
||||
connectivityCount++;
|
||||
}
|
||||
if (job.hasChargingConstraint()) {
|
||||
chargingCount++;
|
||||
}
|
||||
if (job.hasBatteryNotLowConstraint()) {
|
||||
batteryNotLowCount++;
|
||||
}
|
||||
if (job.hasStorageNotLowConstraint()) {
|
||||
storageNotLowCount++;
|
||||
}
|
||||
if (job.hasContentTriggerConstraint()) {
|
||||
contentCount++;
|
||||
}
|
||||
if (runnableJobs == null) {
|
||||
runnableJobs = new ArrayList<>();
|
||||
}
|
||||
runnableJobs.add(job);
|
||||
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
|
||||
stopJobOnServiceContextLocked(job,
|
||||
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
|
||||
}
|
||||
}
|
||||
|
||||
public void postProcess() {
|
||||
if (backoffCount > 0 ||
|
||||
idleCount >= mConstants.MIN_IDLE_COUNT ||
|
||||
connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
|
||||
chargingCount >= mConstants.MIN_CHARGING_COUNT ||
|
||||
batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
|
||||
storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
|
||||
contentCount >= mConstants.MIN_CONTENT_COUNT ||
|
||||
(runnableJobs != null
|
||||
&& runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
|
||||
}
|
||||
noteJobsPending(runnableJobs);
|
||||
mPendingJobs.addAll(runnableJobs);
|
||||
} else {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
|
||||
}
|
||||
}
|
||||
|
||||
// Be ready for next time
|
||||
reset();
|
||||
}
|
||||
|
||||
private void reset() {
|
||||
chargingCount = 0;
|
||||
idleCount = 0;
|
||||
backoffCount = 0;
|
||||
connectivityCount = 0;
|
||||
batteryNotLowCount = 0;
|
||||
storageNotLowCount = 0;
|
||||
contentCount = 0;
|
||||
runnableJobs = null;
|
||||
}
|
||||
}
|
||||
private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
|
||||
|
||||
private void maybeQueueReadyJobsForExecutionLocked() {
|
||||
if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
|
||||
|
||||
noteJobsNonpending(mPendingJobs);
|
||||
mPendingJobs.clear();
|
||||
mJobs.forEachJob(mMaybeQueueFunctor);
|
||||
mMaybeQueueFunctor.postProcess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Criteria for moving a job into the pending queue:
|
||||
* - It's ready.
|
||||
* - It's not pending.
|
||||
* - It's not already running on a JSC.
|
||||
* - The user that requested the job is running.
|
||||
* - The component is enabled and runnable.
|
||||
*/
|
||||
private boolean isReadyToBeExecutedLocked(JobStatus job) {
|
||||
final boolean jobExists = mJobs.containsJob(job);
|
||||
final boolean jobReady = job.isReady();
|
||||
final boolean jobPending = mPendingJobs.contains(job);
|
||||
final boolean jobActive = isCurrentlyActiveLocked(job);
|
||||
final boolean jobBackingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
|
||||
|
||||
final int userId = job.getUserId();
|
||||
final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
|
||||
+ " exists=" + jobExists
|
||||
+ " ready=" + jobReady + " pending=" + jobPending
|
||||
+ " active=" + jobActive + " backingup=" + jobBackingUp
|
||||
+ " userStarted=" + userStarted);
|
||||
}
|
||||
|
||||
// Short circuit: don't do the expensive PM check unless we really think
|
||||
// we might need to run this job now.
|
||||
if (!jobExists || !userStarted || !jobReady || jobPending || jobActive || jobBackingUp) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final boolean componentPresent;
|
||||
try {
|
||||
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
|
||||
job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
|
||||
userId) != null);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowAsRuntimeException();
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
|
||||
+ " componentPresent=" + componentPresent);
|
||||
}
|
||||
|
||||
// Everything else checked out so far, so this is the final yes/no check
|
||||
return componentPresent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Criteria for cancelling an active job:
|
||||
* - It's not ready
|
||||
* - It's running on a JSC.
|
||||
*/
|
||||
private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
|
||||
return !job.isReady() && isCurrentlyActiveLocked(job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reconcile jobs in the pending queue against available execution contexts.
|
||||
* A controller can force a job into the pending queue even if it's already running, but
|
||||
* here is where we decide whether to actually execute it.
|
||||
*/
|
||||
private void maybeRunPendingJobsLocked() {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
|
||||
}
|
||||
assignJobsToContextsLocked();
|
||||
reportActiveLocked();
|
||||
}
|
||||
|
||||
private int adjustJobPriority(int curPriority, JobStatus job) {
|
||||
@@ -1619,6 +1630,38 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
return pendingJobs;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addBackingUpUid(int uid) {
|
||||
synchronized (mLock) {
|
||||
// No need to actually do anything here, since for a full backup the
|
||||
// activity manager will kill the process which will kill the job (and
|
||||
// cause it to restart, but now it can't run).
|
||||
mBackingUpUids.put(uid, uid);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeBackingUpUid(int uid) {
|
||||
synchronized (mLock) {
|
||||
mBackingUpUids.delete(uid);
|
||||
// If there are any jobs for this uid, we need to rebuild the pending list
|
||||
// in case they are now ready to run.
|
||||
if (mJobs.countJobsForUid(uid) > 0) {
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearAllBackingUpUids() {
|
||||
synchronized (mLock) {
|
||||
if (mBackingUpUids.size() > 0) {
|
||||
mBackingUpUids.clear();
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1868,7 +1911,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
|
||||
}
|
||||
|
||||
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
|
||||
queueReadyJobsForExecutionLocked();
|
||||
maybeRunPendingJobsLocked();
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
// can't happen
|
||||
@@ -2015,7 +2059,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
|
||||
job.dump(pw, " ", true);
|
||||
pw.print(" Ready: ");
|
||||
pw.print(mHandler.isReadyToBeExecutedLocked(job));
|
||||
pw.print(isReadyToBeExecutedLocked(job));
|
||||
pw.print(" (job=");
|
||||
pw.print(job.isReady());
|
||||
pw.print(" user=");
|
||||
@@ -2024,6 +2068,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
pw.print(!mPendingJobs.contains(job));
|
||||
pw.print(" !active=");
|
||||
pw.print(!isCurrentlyActiveLocked(job));
|
||||
pw.print(" !backingup=");
|
||||
pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
|
||||
pw.print(" comp=");
|
||||
boolean componentPresent = false;
|
||||
try {
|
||||
@@ -2052,6 +2098,24 @@ public final class JobSchedulerService extends com.android.server.SystemService
|
||||
pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
|
||||
}
|
||||
}
|
||||
if (mBackingUpUids.size() > 0) {
|
||||
pw.println();
|
||||
pw.println("Backing up uids:");
|
||||
boolean first = true;
|
||||
for (int i = 0; i < mBackingUpUids.size(); i++) {
|
||||
int uid = mBackingUpUids.keyAt(i);
|
||||
if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
|
||||
if (first) {
|
||||
pw.print(" ");
|
||||
first = false;
|
||||
} else {
|
||||
pw.print(", ");
|
||||
}
|
||||
pw.print(UserHandle.formatUid(uid));
|
||||
}
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
pw.println();
|
||||
mJobPackageTracker.dump(pw, "", filterUidFinal);
|
||||
pw.println();
|
||||
|
||||
@@ -123,7 +123,8 @@ public class AppIdleController extends StateController {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
|
||||
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.os.BatteryManager;
|
||||
import android.os.BatteryManagerInternal;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -32,9 +33,6 @@ import com.android.server.job.JobSchedulerService;
|
||||
import com.android.server.job.StateChangedListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple controller that tracks whether the phone is charging or not. The phone is considered to
|
||||
@@ -47,7 +45,7 @@ public class BatteryController extends StateController {
|
||||
private static final Object sCreationLock = new Object();
|
||||
private static volatile BatteryController sController;
|
||||
|
||||
private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
|
||||
private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
|
||||
private ChargingTracker mChargeTracker;
|
||||
|
||||
public static BatteryController get(JobSchedulerService taskManagerService) {
|
||||
@@ -82,6 +80,7 @@ public class BatteryController extends StateController {
|
||||
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
|
||||
if (taskStatus.hasPowerConstraint()) {
|
||||
mTrackedTasks.add(taskStatus);
|
||||
taskStatus.setTrackingController(JobStatus.TRACKING_BATTERY);
|
||||
taskStatus.setChargingConstraintSatisfied(mChargeTracker.isOnStablePower());
|
||||
taskStatus.setBatteryNotLowConstraintSatisfied(mChargeTracker.isBatteryNotLow());
|
||||
}
|
||||
@@ -89,7 +88,7 @@ public class BatteryController extends StateController {
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
|
||||
if (taskStatus.hasPowerConstraint()) {
|
||||
if (taskStatus.clearTrackingController(JobStatus.TRACKING_BATTERY)) {
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +102,7 @@ public class BatteryController extends StateController {
|
||||
boolean reportChange = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
|
||||
final JobStatus ts = mTrackedTasks.get(i);
|
||||
final JobStatus ts = mTrackedTasks.valueAt(i);
|
||||
boolean previous = ts.setChargingConstraintSatisfied(stablePower);
|
||||
if (previous != stablePower) {
|
||||
reportChange = true;
|
||||
@@ -251,7 +250,7 @@ public class BatteryController extends StateController {
|
||||
pw.print(mTrackedTasks.size());
|
||||
pw.println(":");
|
||||
for (int i = 0; i < mTrackedTasks.size(); i++) {
|
||||
final JobStatus js = mTrackedTasks.get(i);
|
||||
final JobStatus js = mTrackedTasks.valueAt(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.net.NetworkInfo;
|
||||
import android.net.NetworkPolicyManager;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
@@ -34,7 +35,6 @@ import com.android.server.job.JobSchedulerService;
|
||||
import com.android.server.job.StateChangedListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Handles changes in connectivity.
|
||||
@@ -54,7 +54,7 @@ public class ConnectivityController extends StateController implements
|
||||
private boolean mValidated;
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final ArrayList<JobStatus> mTrackedJobs = new ArrayList<JobStatus>();
|
||||
private final ArraySet<JobStatus> mTrackedJobs = new ArraySet<>();
|
||||
|
||||
/** Singleton. */
|
||||
private static ConnectivityController mSingleton;
|
||||
@@ -87,13 +87,14 @@ public class ConnectivityController extends StateController implements
|
||||
if (jobStatus.hasConnectivityConstraint()) {
|
||||
updateConstraintsSatisfied(jobStatus, null);
|
||||
mTrackedJobs.add(jobStatus);
|
||||
jobStatus.setTrackingController(JobStatus.TRACKING_CONNECTIVITY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
if (jobStatus.hasConnectivityConstraint()) {
|
||||
if (jobStatus.clearTrackingController(JobStatus.TRACKING_CONNECTIVITY)) {
|
||||
mTrackedJobs.remove(jobStatus);
|
||||
}
|
||||
}
|
||||
@@ -150,8 +151,8 @@ public class ConnectivityController extends StateController implements
|
||||
private void updateTrackedJobs(int uid, NetworkCapabilities capabilities) {
|
||||
synchronized (mLock) {
|
||||
boolean changed = false;
|
||||
for (int i = 0; i < mTrackedJobs.size(); i++) {
|
||||
final JobStatus js = mTrackedJobs.get(i);
|
||||
for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
|
||||
final JobStatus js = mTrackedJobs.valueAt(i);
|
||||
if (uid == -1 || uid == js.getSourceUid()) {
|
||||
changed |= updateConstraintsSatisfied(js, capabilities);
|
||||
}
|
||||
@@ -168,8 +169,8 @@ public class ConnectivityController extends StateController implements
|
||||
@Override
|
||||
public void onNetworkActive() {
|
||||
synchronized (mLock) {
|
||||
for (int i = 0; i < mTrackedJobs.size(); i++) {
|
||||
final JobStatus js = mTrackedJobs.get(i);
|
||||
for (int i = mTrackedJobs.size()-1; i >= 0; i--) {
|
||||
final JobStatus js = mTrackedJobs.valueAt(i);
|
||||
if (js.isReady()) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "Running " + js + " due to network activity.");
|
||||
@@ -239,7 +240,7 @@ public class ConnectivityController extends StateController implements
|
||||
pw.print(mTrackedJobs.size());
|
||||
pw.println(":");
|
||||
for (int i = 0; i < mTrackedJobs.size(); i++) {
|
||||
final JobStatus js = mTrackedJobs.get(i);
|
||||
final JobStatus js = mTrackedJobs.valueAt(i);
|
||||
if (js.shouldDump(filterUid)) {
|
||||
pw.print(" #");
|
||||
js.printUniqueId(pw);
|
||||
|
||||
@@ -35,9 +35,6 @@ import com.android.server.job.StateChangedListener;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Controller for monitoring changes to content URIs through a ContentObserver.
|
||||
@@ -61,11 +58,11 @@ public class ContentObserverController extends StateController {
|
||||
private static final Object sCreationLock = new Object();
|
||||
private static volatile ContentObserverController sController;
|
||||
|
||||
final private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
|
||||
final private ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
|
||||
/**
|
||||
* Per-userid {@link JobInfo.TriggerContentUri} keyed ContentObserver cache.
|
||||
*/
|
||||
SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
|
||||
final SparseArray<ArrayMap<JobInfo.TriggerContentUri, ObserverInstance>> mObservers =
|
||||
new SparseArray<>();
|
||||
final Handler mHandler;
|
||||
|
||||
@@ -101,6 +98,7 @@ public class ContentObserverController extends StateController {
|
||||
Slog.i(TAG, "Tracking content-trigger job " + taskStatus);
|
||||
}
|
||||
mTrackedTasks.add(taskStatus);
|
||||
taskStatus.setTrackingController(JobStatus.TRACKING_CONTENT);
|
||||
boolean havePendingUris = false;
|
||||
// If there is a previous job associated with the new job, propagate over
|
||||
// any pending content URI trigger reports.
|
||||
@@ -156,7 +154,8 @@ public class ContentObserverController extends StateController {
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
if (taskStatus.hasContentTriggerConstraint()) {
|
||||
if (taskStatus.clearTrackingController(JobStatus.TRACKING_CONTENT)) {
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
if (taskStatus.contentObserverJobInstance != null) {
|
||||
taskStatus.contentObserverJobInstance.unscheduleLocked();
|
||||
if (incomingJob != null) {
|
||||
@@ -190,7 +189,6 @@ public class ContentObserverController extends StateController {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "No longer tracking job " + taskStatus);
|
||||
}
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,9 +372,8 @@ public class ContentObserverController extends StateController {
|
||||
@Override
|
||||
public void dumpControllerStateLocked(PrintWriter pw, int filterUid) {
|
||||
pw.println("Content:");
|
||||
Iterator<JobStatus> it = mTrackedTasks.iterator();
|
||||
while (it.hasNext()) {
|
||||
JobStatus js = it.next();
|
||||
for (int i = 0; i < mTrackedTasks.size(); i++) {
|
||||
JobStatus js = mTrackedTasks.valueAt(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -164,13 +164,12 @@ public class DeviceIdleJobsController extends StateController {
|
||||
|
||||
@Override
|
||||
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
|
||||
synchronized (mLock) {
|
||||
updateTaskStateLocked(jobStatus);
|
||||
}
|
||||
updateTaskStateLocked(jobStatus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean forUpdate) {
|
||||
public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package com.android.server.job.controllers;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.PendingIntent;
|
||||
@@ -27,6 +26,7 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
@@ -40,7 +40,7 @@ public class IdleController extends StateController {
|
||||
// screen off or dreaming for at least this long
|
||||
private long mInactivityIdleThreshold;
|
||||
private long mIdleWindowSlop;
|
||||
final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
|
||||
final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
|
||||
IdlenessTracker mIdleTracker;
|
||||
|
||||
// Singleton factory
|
||||
@@ -69,13 +69,17 @@ public class IdleController extends StateController {
|
||||
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
|
||||
if (taskStatus.hasIdleConstraint()) {
|
||||
mTrackedTasks.add(taskStatus);
|
||||
taskStatus.setTrackingController(JobStatus.TRACKING_IDLE);
|
||||
taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
if (taskStatus.clearTrackingController(JobStatus.TRACKING_IDLE)) {
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -83,8 +87,8 @@ public class IdleController extends StateController {
|
||||
*/
|
||||
void reportNewIdleState(boolean isIdle) {
|
||||
synchronized (mLock) {
|
||||
for (JobStatus task : mTrackedTasks) {
|
||||
task.setIdleConstraintSatisfied(isIdle);
|
||||
for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
|
||||
mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(isIdle);
|
||||
}
|
||||
}
|
||||
mStateChangedListener.onControllerStateChanged();
|
||||
@@ -200,7 +204,7 @@ public class IdleController extends StateController {
|
||||
pw.print(mTrackedTasks.size());
|
||||
pw.println(":");
|
||||
for (int i = 0; i < mTrackedTasks.size(); i++) {
|
||||
final JobStatus js = mTrackedTasks.get(i);
|
||||
final JobStatus js = mTrackedTasks.valueAt(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -128,6 +128,38 @@ public final class JobStatus {
|
||||
// Set to true if doze constraint was satisfied due to app being whitelisted.
|
||||
public boolean dozeWhitelisted;
|
||||
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the battery controller is currently tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_BATTERY = 1<<0;
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the network connectivity controller is currently
|
||||
* tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_CONNECTIVITY = 1<<1;
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the content observer controller is currently
|
||||
* tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_CONTENT = 1<<2;
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the idle controller is currently tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_IDLE = 1<<3;
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the storage controller is currently tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_STORAGE = 1<<4;
|
||||
/**
|
||||
* Flag for {@link #trackingControllers}: the time controller is currently tracking this job.
|
||||
*/
|
||||
public static final int TRACKING_TIME = 1<<5;
|
||||
|
||||
/**
|
||||
* Bit mask of controllers that are currently tracking the job.
|
||||
*/
|
||||
private int trackingControllers;
|
||||
|
||||
// These are filled in by controllers when preparing for execution.
|
||||
public ArraySet<Uri> changedUris;
|
||||
public ArraySet<String> changedAuthorities;
|
||||
@@ -609,6 +641,18 @@ public final class JobStatus {
|
||||
return (satisfiedConstraints&constraint) != 0;
|
||||
}
|
||||
|
||||
boolean clearTrackingController(int which) {
|
||||
if ((trackingControllers&which) != 0) {
|
||||
trackingControllers &= ~which;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void setTrackingController(int which) {
|
||||
trackingControllers |= which;
|
||||
}
|
||||
|
||||
public boolean shouldDump(int filterUid) {
|
||||
return filterUid == -1 || UserHandle.getAppId(getUid()) == filterUid
|
||||
|| UserHandle.getAppId(getSourceUid()) == filterUid;
|
||||
@@ -925,6 +969,16 @@ public final class JobStatus {
|
||||
pw.print(prefix); pw.println("Doze whitelisted: true");
|
||||
}
|
||||
}
|
||||
if (trackingControllers != 0) {
|
||||
pw.print(prefix); pw.print("Tracking:");
|
||||
if ((trackingControllers&TRACKING_BATTERY) != 0) pw.print(" BATTERY");
|
||||
if ((trackingControllers&TRACKING_CONNECTIVITY) != 0) pw.print(" CONNECTIVITY");
|
||||
if ((trackingControllers&TRACKING_CONTENT) != 0) pw.print(" CONTENT");
|
||||
if ((trackingControllers&TRACKING_IDLE) != 0) pw.print(" IDLE");
|
||||
if ((trackingControllers&TRACKING_STORAGE) != 0) pw.print(" STORAGE");
|
||||
if ((trackingControllers&TRACKING_TIME) != 0) pw.print(" TIME");
|
||||
pw.println();
|
||||
}
|
||||
if (changedAuthorities != null) {
|
||||
pw.print(prefix); pw.println("Changed authorities:");
|
||||
for (int i=0; i<changedAuthorities.size(); i++) {
|
||||
|
||||
@@ -20,9 +20,9 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.BatteryManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
@@ -31,8 +31,6 @@ import com.android.server.job.StateChangedListener;
|
||||
import com.android.server.storage.DeviceStorageMonitorService;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Simple controller that tracks the status of the device's storage.
|
||||
@@ -43,7 +41,7 @@ public class StorageController extends StateController {
|
||||
private static final Object sCreationLock = new Object();
|
||||
private static volatile StorageController sController;
|
||||
|
||||
private List<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
|
||||
private final ArraySet<JobStatus> mTrackedTasks = new ArraySet<JobStatus>();
|
||||
private StorageTracker mStorageTracker;
|
||||
|
||||
public static StorageController get(JobSchedulerService taskManagerService) {
|
||||
@@ -78,13 +76,15 @@ public class StorageController extends StateController {
|
||||
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
|
||||
if (taskStatus.hasStorageNotLowConstraint()) {
|
||||
mTrackedTasks.add(taskStatus);
|
||||
taskStatus.setTrackingController(JobStatus.TRACKING_STORAGE);
|
||||
taskStatus.setStorageNotLowConstraintSatisfied(mStorageTracker.isStorageNotLow());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob, boolean forUpdate) {
|
||||
if (taskStatus.hasPowerConstraint()) {
|
||||
public void maybeStopTrackingJobLocked(JobStatus taskStatus, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
if (taskStatus.clearTrackingController(JobStatus.TRACKING_STORAGE)) {
|
||||
mTrackedTasks.remove(taskStatus);
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ public class StorageController extends StateController {
|
||||
boolean reportChange = false;
|
||||
synchronized (mLock) {
|
||||
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
|
||||
final JobStatus ts = mTrackedTasks.get(i);
|
||||
final JobStatus ts = mTrackedTasks.valueAt(i);
|
||||
boolean previous = ts.setStorageNotLowConstraintSatisfied(storageNotLow);
|
||||
if (previous != storageNotLow) {
|
||||
reportChange = true;
|
||||
@@ -178,7 +178,7 @@ public class StorageController extends StateController {
|
||||
pw.print(mTrackedTasks.size());
|
||||
pw.println(":");
|
||||
for (int i = 0; i < mTrackedTasks.size(); i++) {
|
||||
final JobStatus js = mTrackedTasks.get(i);
|
||||
final JobStatus js = mTrackedTasks.valueAt(i);
|
||||
if (!js.shouldDump(filterUid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class TimeController extends StateController {
|
||||
|
||||
private AlarmManager mAlarmService = null;
|
||||
/** List of tracked jobs, sorted asc. by deadline */
|
||||
private final List<JobStatus> mTrackedJobs = new LinkedList<JobStatus>();
|
||||
private final List<JobStatus> mTrackedJobs = new LinkedList<>();
|
||||
/** Singleton. */
|
||||
private static TimeController mSingleton;
|
||||
|
||||
@@ -78,6 +78,20 @@ public class TimeController extends StateController {
|
||||
public void maybeStartTrackingJobLocked(JobStatus job, JobStatus lastJob) {
|
||||
if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
|
||||
maybeStopTrackingJobLocked(job, null, false);
|
||||
|
||||
// First: check the constraints now, because if they are already satisfied
|
||||
// then there is no need to track it. This gives us a fast path for a common
|
||||
// pattern of having a job with a 0 deadline constraint ("run immediately").
|
||||
// Unlike most controllers, once one of our constraints has been satisfied, it
|
||||
// will never be unsatisfied (our time base can not go backwards).
|
||||
final long nowElapsedMillis = SystemClock.elapsedRealtime();
|
||||
if (job.hasDeadlineConstraint() && evaluateDeadlineConstraint(job, nowElapsedMillis)) {
|
||||
return;
|
||||
} else if (job.hasTimingDelayConstraint() && evaluateTimingDelayConstraint(job,
|
||||
nowElapsedMillis)) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isInsert = false;
|
||||
ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
|
||||
while (it.hasPrevious()) {
|
||||
@@ -92,6 +106,7 @@ public class TimeController extends StateController {
|
||||
it.next();
|
||||
}
|
||||
it.add(job);
|
||||
job.setTrackingController(JobStatus.TRACKING_TIME);
|
||||
maybeUpdateAlarmsLocked(
|
||||
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
|
||||
job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE,
|
||||
@@ -102,13 +117,15 @@ public class TimeController extends StateController {
|
||||
/**
|
||||
* When we stop tracking a job, we only need to update our alarms if the job we're no longer
|
||||
* tracking was the one our alarms were based off of.
|
||||
* Really an == comparison should be enough, but why play with fate? We'll do <=.
|
||||
*/
|
||||
@Override
|
||||
public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob, boolean forUpdate) {
|
||||
if (mTrackedJobs.remove(job)) {
|
||||
checkExpiredDelaysAndResetAlarm();
|
||||
checkExpiredDeadlinesAndResetAlarm();
|
||||
public void maybeStopTrackingJobLocked(JobStatus job, JobStatus incomingJob,
|
||||
boolean forUpdate) {
|
||||
if (job.clearTrackingController(JobStatus.TRACKING_TIME)) {
|
||||
if (mTrackedJobs.remove(job)) {
|
||||
checkExpiredDelaysAndResetAlarm();
|
||||
checkExpiredDeadlinesAndResetAlarm();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,17 +164,12 @@ public class TimeController extends StateController {
|
||||
if (!job.hasDeadlineConstraint()) {
|
||||
continue;
|
||||
}
|
||||
final long jobDeadline = job.getLatestRunTimeElapsed();
|
||||
|
||||
if (jobDeadline <= nowElapsedMillis) {
|
||||
if (job.hasTimingDelayConstraint()) {
|
||||
job.setTimingDelayConstraintSatisfied(true);
|
||||
}
|
||||
job.setDeadlineConstraintSatisfied(true);
|
||||
if (evaluateDeadlineConstraint(job, nowElapsedMillis)) {
|
||||
mStateChangedListener.onRunJobNow(job);
|
||||
it.remove();
|
||||
} else { // Sorted by expiry time, so take the next one and stop.
|
||||
nextExpiryTime = jobDeadline;
|
||||
nextExpiryTime = job.getLatestRunTimeElapsed();
|
||||
nextExpiryUid = job.getSourceUid();
|
||||
break;
|
||||
}
|
||||
@@ -166,6 +178,19 @@ public class TimeController extends StateController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean evaluateDeadlineConstraint(JobStatus job, long nowElapsedMillis) {
|
||||
final long jobDeadline = job.getLatestRunTimeElapsed();
|
||||
|
||||
if (jobDeadline <= nowElapsedMillis) {
|
||||
if (job.hasTimingDelayConstraint()) {
|
||||
job.setTimingDelayConstraintSatisfied(true);
|
||||
}
|
||||
job.setDeadlineConstraintSatisfied(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles alarm that notifies us that a job's delay has expired. Iterates through the list of
|
||||
* tracked jobs and marks them as ready as appropriate.
|
||||
@@ -182,9 +207,7 @@ public class TimeController extends StateController {
|
||||
if (!job.hasTimingDelayConstraint()) {
|
||||
continue;
|
||||
}
|
||||
final long jobDelayTime = job.getEarliestRunTime();
|
||||
if (jobDelayTime <= nowElapsedMillis) {
|
||||
job.setTimingDelayConstraintSatisfied(true);
|
||||
if (evaluateTimingDelayConstraint(job, nowElapsedMillis)) {
|
||||
if (canStopTrackingJobLocked(job)) {
|
||||
it.remove();
|
||||
}
|
||||
@@ -194,6 +217,7 @@ public class TimeController extends StateController {
|
||||
} else if (!job.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY)) {
|
||||
// If this job still doesn't have its delay constraint satisfied,
|
||||
// then see if it is the next upcoming delay time for the alarm.
|
||||
final long jobDelayTime = job.getEarliestRunTime();
|
||||
if (nextDelayTime > jobDelayTime) {
|
||||
nextDelayTime = jobDelayTime;
|
||||
nextDelayUid = job.getSourceUid();
|
||||
@@ -207,6 +231,15 @@ public class TimeController extends StateController {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean evaluateTimingDelayConstraint(JobStatus job, long nowElapsedMillis) {
|
||||
final long jobDelayTime = job.getEarliestRunTime();
|
||||
if (jobDelayTime <= nowElapsedMillis) {
|
||||
job.setTimingDelayConstraintSatisfied(true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed,
|
||||
int uid) {
|
||||
if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
|
||||
|
||||
Reference in New Issue
Block a user