Remove internal locking from JobStatus.

Now all state of JobStatus is implicitly protected by the lock
of whoever is using it -- in this case the global lock for the
JobSchedulerService.  This allows us to remove all of the atomic
variables and just replace those with a simple bit field.

The required constraints for a job are now statically defined
once a JobStatus is created, and don't change.  (They wouldn't
change before, but now this is absolutely specified to be the
case.)  This required tweaking the constructors a bit so that
the earliest and latest run times are computed as part of the
core class initialization.

Also clarified methods on StateController that are called with
the lock held, and took advantage of that in the various
controllers to not now redundantly re-acquire the lock.

Change-Id: I595c5e7d1bff1bd2ff906d612581af82878a25ee
This commit is contained in:
Dianne Hackborn
2016-02-16 10:30:33 -08:00
parent 9d3a4e1c31
commit b0001f6fb1
11 changed files with 426 additions and 413 deletions

View File

@ -237,7 +237,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId) {
JobStatus jobStatus = new JobStatus(getLock(), job, uId, packageName, userId);
JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId);
try {
if (ActivityManagerNative.getDefault().getAppStartMode(uId,
job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
@ -476,7 +476,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
JobStatus job = jobs.valueAt(i);
for (int controller=0; controller<mControllers.size(); controller++) {
mControllers.get(controller).deviceIdleModeChanged(mDeviceIdleMode);
mControllers.get(controller).maybeStartTrackingJob(job, null);
mControllers.get(controller).maybeStartTrackingJobLocked(job, null);
}
}
// GO GO GO!
@ -491,19 +491,16 @@ public final class JobSchedulerService extends com.android.server.SystemService
* about.
*/
private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
boolean update;
boolean rocking;
synchronized (mLock) {
update = mJobs.add(jobStatus);
rocking = mReadyToRock;
}
if (rocking) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
if (update) {
controller.maybeStopTrackingJob(jobStatus, true);
final boolean update = mJobs.add(jobStatus);
if (mReadyToRock) {
for (int i = 0; i < mControllers.size(); i++) {
StateController controller = mControllers.get(i);
if (update) {
controller.maybeStopTrackingJobLocked(jobStatus, true);
}
controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
}
controller.maybeStartTrackingJob(jobStatus, lastJob);
}
}
}
@ -513,20 +510,17 @@ public final class JobSchedulerService extends com.android.server.SystemService
* object removed.
*/
private boolean stopTrackingJob(JobStatus jobStatus, boolean writeBack) {
boolean removed;
boolean rocking;
synchronized (mLock) {
// Remove from store as well as controllers.
removed = mJobs.remove(jobStatus, writeBack);
rocking = mReadyToRock;
}
if (removed && rocking) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJob(jobStatus, false);
final boolean removed = mJobs.remove(jobStatus, writeBack);
if (removed && mReadyToRock) {
for (int i=0; i<mControllers.size(); i++) {
StateController controller = mControllers.get(i);
controller.maybeStopTrackingJobLocked(jobStatus, false);
}
}
return removed;
}
return removed;
}
private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
@ -759,7 +753,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, " queued " + job.toShortString());
}
mPendingJobs.add(job);
} else if (areJobConstraintsNotSatisfied(job)) {
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
stopJobOnServiceContextLocked(job,
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
}
@ -826,7 +820,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
runnableJobs = new ArrayList<>();
}
runnableJobs.add(job);
} else if (areJobConstraintsNotSatisfied(job)) {
} else if (areJobConstraintsNotSatisfiedLocked(job)) {
stopJobOnServiceContextLocked(job,
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
}
@ -875,7 +869,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
* - It's not ready
* - It's running on a JSC.
*/
private boolean areJobConstraintsNotSatisfied(JobStatus job) {
private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
return !job.isReady() && isCurrentlyActiveLocked(job);
}
@ -893,7 +887,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
if (DEBUG) {
Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
}
assignJobsToContextsH();
assignJobsToContextsLocked();
reportActive();
}
}
@ -905,7 +899,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
* run higher priority ones.
* Lock on mJobs before calling this function.
*/
private void assignJobsToContextsH() {
private void assignJobsToContextsLocked() {
if (DEBUG) {
Slog.d(TAG, printPendingQueue());
}
@ -990,7 +984,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
for (int ic=0; ic<mControllers.size(); ic++) {
StateController controller = mControllers.get(ic);
controller.prepareForExecution(contextIdToJobMap[i]);
controller.prepareForExecutionLocked(contextIdToJobMap[i]);
}
if (!mActiveServices.get(i).executeRunnableJob(contextIdToJobMap[i])) {
Slog.d(TAG, "Error executing " + contextIdToJobMap[i]);
@ -1222,7 +1216,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
for (int i=0; i<mControllers.size(); i++) {
pw.println();
mControllers.get(i).dumpControllerState(pw);
mControllers.get(i).dumpControllerStateLocked(pw);
}
pw.println();
pw.println(printPendingQueue());

View File

@ -44,7 +44,6 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -680,7 +679,7 @@ public class JobStore {
parser.nextTag(); // Consume </extras>
JobStatus js = new JobStatus(
mLock, jobBuilder.build(), uid, sourcePackageName, sourceUserId,
jobBuilder.build(), uid, sourcePackageName, sourceUserId,
elapsedRuntimes.first, elapsedRuntimes.second);
return js;
}

View File

@ -64,39 +64,34 @@ public class AppIdleController extends StateController {
}
@Override
public void maybeStartTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
synchronized (mLock) {
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.getSourcePackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
jobStatus.getSourceUid(), jobStatus.getSourceUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+ packageName + " to " + appIdle);
}
jobStatus.appNotIdleConstraintSatisfied.set(!appIdle);
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
mTrackedTasks.add(jobStatus);
String packageName = jobStatus.getSourcePackageName();
final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName,
jobStatus.getSourceUid(), jobStatus.getSourceUserId());
if (DEBUG) {
Slog.d(LOG_TAG, "Start tracking, setting idle state of "
+ packageName + " to " + appIdle);
}
jobStatus.setAppNotIdleConstraintSatisfied(!appIdle);
}
@Override
public void maybeStopTrackingJob(JobStatus jobStatus, boolean forUpdate) {
synchronized (mLock) {
mTrackedTasks.remove(jobStatus);
}
public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
mTrackedTasks.remove(jobStatus);
}
@Override
public void dumpControllerState(PrintWriter pw) {
public void dumpControllerStateLocked(PrintWriter pw) {
pw.println("AppIdle");
pw.println("Parole On: " + mAppIdleParoleOn);
synchronized (mLock) {
for (JobStatus task : mTrackedTasks) {
pw.print(task.getSourcePackageName());
pw.print(":idle=" + !task.appNotIdleConstraintSatisfied.get());
pw.print(", ");
}
pw.println();
for (JobStatus task : mTrackedTasks) {
pw.print(task.getSourcePackageName());
pw.print(":idle="
+ ((task.satisfiedConstraints&JobStatus.CONSTRAINT_APP_NOT_IDLE) != 0));
pw.print(", ");
}
pw.println();
}
void setAppIdleParoleOn(boolean isAppIdleParoleOn) {
@ -114,8 +109,7 @@ public class AppIdleController extends StateController {
if (DEBUG) {
Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle);
}
if (task.appNotIdleConstraintSatisfied.get() == appIdle) {
task.appNotIdleConstraintSatisfied.set(!appIdle);
if (task.setAppNotIdleConstraintSatisfied(!appIdle)) {
changed = true;
}
}
@ -137,12 +131,11 @@ public class AppIdleController extends StateController {
for (JobStatus task : mTrackedTasks) {
if (task.getSourcePackageName().equals(packageName)
&& task.getSourceUserId() == userId) {
if (task.appNotIdleConstraintSatisfied.get() != !idle) {
if (task.setAppNotIdleConstraintSatisfied(!idle)) {
if (DEBUG) {
Slog.d(LOG_TAG, "App Idle state changed, setting idle state of "
+ packageName + " to " + idle);
}
task.appNotIdleConstraintSatisfied.set(!idle);
changed = true;
}
}

View File

@ -78,22 +78,18 @@ public class BatteryController extends StateController {
}
@Override
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
final boolean isOnStablePower = mChargeTracker.isOnStablePower();
if (taskStatus.hasChargingConstraint()) {
synchronized (mLock) {
mTrackedTasks.add(taskStatus);
taskStatus.chargingConstraintSatisfied.set(isOnStablePower);
}
mTrackedTasks.add(taskStatus);
taskStatus.setChargingConstraintSatisfied(isOnStablePower);
}
}
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
if (taskStatus.hasChargingConstraint()) {
synchronized (mLock) {
mTrackedTasks.remove(taskStatus);
}
mTrackedTasks.remove(taskStatus);
}
}
@ -105,7 +101,7 @@ public class BatteryController extends StateController {
boolean reportChange = false;
synchronized (mLock) {
for (JobStatus ts : mTrackedTasks) {
boolean previous = ts.chargingConstraintSatisfied.getAndSet(stablePower);
boolean previous = ts.setChargingConstraintSatisfied(stablePower);
if (previous != stablePower) {
reportChange = true;
}
@ -198,18 +194,16 @@ public class BatteryController extends StateController {
}
@Override
public void dumpControllerState(PrintWriter pw) {
public void dumpControllerStateLocked(PrintWriter pw) {
pw.println("Batt.");
pw.println("Stable power: " + mChargeTracker.isOnStablePower());
synchronized (mLock) {
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
}
while (it.hasNext()) {
pw.print("," + String.valueOf(it.next().hashCode()));
}
pw.println();
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
}
while (it.hasNext()) {
pw.print("," + String.valueOf(it.next().hashCode()));
}
pw.println();
}
}

View File

@ -83,22 +83,18 @@ public class ConnectivityController extends StateController implements
}
@Override
public void maybeStartTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
synchronized (mLock) {
jobStatus.connectivityConstraintSatisfied.set(mNetworkConnected);
jobStatus.unmeteredConstraintSatisfied.set(mNetworkUnmetered);
mTrackedJobs.add(jobStatus);
}
jobStatus.setConnectivityConstraintSatisfied(mNetworkConnected);
jobStatus.setUnmeteredConstraintSatisfied(mNetworkUnmetered);
mTrackedJobs.add(jobStatus);
}
}
@Override
public void maybeStopTrackingJob(JobStatus jobStatus, boolean forUpdate) {
public void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate) {
if (jobStatus.hasConnectivityConstraint() || jobStatus.hasUnmeteredConstraint()) {
synchronized (mLock) {
mTrackedJobs.remove(jobStatus);
}
mTrackedJobs.remove(jobStatus);
}
}
@ -112,12 +108,8 @@ public class ConnectivityController extends StateController implements
if (js.getUserId() != userId) {
continue;
}
boolean prevIsConnected =
js.connectivityConstraintSatisfied.getAndSet(mNetworkConnected);
boolean prevIsMetered = js.unmeteredConstraintSatisfied.getAndSet(mNetworkUnmetered);
if (prevIsConnected != mNetworkConnected || prevIsMetered != mNetworkUnmetered) {
changed = true;
}
changed |= js.setConnectivityConstraintSatisfied(mNetworkConnected);
changed |= js.setUnmeteredConstraintSatisfied(mNetworkUnmetered);
}
if (changed) {
mStateChangedListener.onControllerStateChanged();
@ -189,7 +181,7 @@ public class ConnectivityController extends StateController implements
};
@Override
public void dumpControllerState(PrintWriter pw) {
public void dumpControllerStateLocked(PrintWriter pw) {
pw.println("Conn.");
pw.println("connected: " + mNetworkConnected + " unmetered: " + mNetworkUnmetered);
for (JobStatus js: mTrackedJobs) {

View File

@ -75,87 +75,81 @@ public class ContentObserverController extends StateController {
}
@Override
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasContentTriggerConstraint()) {
synchronized (mLock) {
if (taskStatus.contentObserverJobInstance == null) {
taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
}
mTrackedTasks.add(taskStatus);
boolean havePendingUris = false;
// If there is a previous job associated with the new job, propagate over
// any pending content URI trigger reports.
if (lastJob != null && lastJob.contentObserverJobInstance != null
&& lastJob.contentObserverJobInstance
!= taskStatus.contentObserverJobInstance
&& lastJob.contentObserverJobInstance.mChangedAuthorities != null) {
havePendingUris = true;
if (taskStatus.contentObserverJobInstance == null) {
taskStatus.contentObserverJobInstance = new JobInstance(taskStatus);
}
mTrackedTasks.add(taskStatus);
boolean havePendingUris = false;
// If there is a previous job associated with the new job, propagate over
// any pending content URI trigger reports.
if (lastJob != null && lastJob.contentObserverJobInstance != null
&& lastJob.contentObserverJobInstance
!= taskStatus.contentObserverJobInstance
&& lastJob.contentObserverJobInstance.mChangedAuthorities != null) {
havePendingUris = true;
taskStatus.contentObserverJobInstance.mChangedAuthorities
= lastJob.contentObserverJobInstance.mChangedAuthorities;
taskStatus.contentObserverJobInstance.mChangedUris
= lastJob.contentObserverJobInstance.mChangedUris;
lastJob.contentObserverJobInstance.mChangedAuthorities = null;
lastJob.contentObserverJobInstance.mChangedUris = null;
}
// If we have previously reported changed authorities/uris, then we failed
// to complete the job with them so will re-record them to report again.
if (taskStatus.changedAuthorities != null) {
havePendingUris = true;
if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
taskStatus.contentObserverJobInstance.mChangedAuthorities
= lastJob.contentObserverJobInstance.mChangedAuthorities;
taskStatus.contentObserverJobInstance.mChangedUris
= lastJob.contentObserverJobInstance.mChangedUris;
lastJob.contentObserverJobInstance.mChangedAuthorities = null;
lastJob.contentObserverJobInstance.mChangedUris = null;
= new ArraySet<>();
}
// If we have previously reported changed authorities/uris, then we failed
// to complete the job with them so will re-record them to report again.
if (taskStatus.changedAuthorities != null) {
havePendingUris = true;
if (taskStatus.contentObserverJobInstance.mChangedAuthorities == null) {
taskStatus.contentObserverJobInstance.mChangedAuthorities
= new ArraySet<>();
for (String auth : taskStatus.changedAuthorities) {
taskStatus.contentObserverJobInstance.mChangedAuthorities.add(auth);
}
if (taskStatus.changedUris != null) {
if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
taskStatus.contentObserverJobInstance.mChangedUris = new ArraySet<>();
}
for (String auth : taskStatus.changedAuthorities) {
taskStatus.contentObserverJobInstance.mChangedAuthorities.add(auth);
for (Uri uri : taskStatus.changedUris) {
taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
}
if (taskStatus.changedUris != null) {
if (taskStatus.contentObserverJobInstance.mChangedUris == null) {
taskStatus.contentObserverJobInstance.mChangedUris = new ArraySet<>();
}
for (Uri uri : taskStatus.changedUris) {
taskStatus.contentObserverJobInstance.mChangedUris.add(uri);
}
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
taskStatus.contentTriggerConstraintSatisfied.set(havePendingUris);
}
taskStatus.changedAuthorities = null;
taskStatus.changedUris = null;
taskStatus.setContentTriggerConstraintSatisfied(havePendingUris);
}
}
@Override
public void prepareForExecutionLocked(JobStatus taskStatus) {
if (taskStatus.hasContentTriggerConstraint()) {
if (taskStatus.contentObserverJobInstance != null) {
taskStatus.changedUris = taskStatus.contentObserverJobInstance.mChangedUris;
taskStatus.changedAuthorities
= taskStatus.contentObserverJobInstance.mChangedAuthorities;
taskStatus.contentObserverJobInstance.mChangedUris = null;
taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
}
}
}
@Override
public void prepareForExecution(JobStatus taskStatus) {
public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
if (taskStatus.hasContentTriggerConstraint()) {
synchronized (mLock) {
if (!forUpdate) {
// We won't do this reset if being called for an update, because
// we know it will be immediately followed by maybeStartTrackingJobLocked...
// and we don't want to lose any content changes in-between.
if (taskStatus.contentObserverJobInstance != null) {
taskStatus.changedUris = taskStatus.contentObserverJobInstance.mChangedUris;
taskStatus.changedAuthorities
= taskStatus.contentObserverJobInstance.mChangedAuthorities;
taskStatus.contentObserverJobInstance.mChangedUris = null;
taskStatus.contentObserverJobInstance.mChangedAuthorities = null;
taskStatus.contentObserverJobInstance.detach();
taskStatus.contentObserverJobInstance = null;
}
}
}
}
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
if (taskStatus.hasContentTriggerConstraint()) {
synchronized (mLock) {
if (!forUpdate) {
// We won't do this reset if being called for an update, because
// we know it will be immediately followed by maybeStartTrackingJob...
// and we don't want to lose any content changes in-between.
if (taskStatus.contentObserverJobInstance != null) {
taskStatus.contentObserverJobInstance.detach();
taskStatus.contentObserverJobInstance = null;
}
}
mTrackedTasks.remove(taskStatus);
}
mTrackedTasks.remove(taskStatus);
}
}
@ -199,9 +193,7 @@ public class ContentObserverController extends StateController {
inst.mChangedAuthorities = new ArraySet<>();
}
inst.mChangedAuthorities.add(uri.getAuthority());
boolean previous
= inst.mJobStatus.contentTriggerConstraintSatisfied.getAndSet(true);
if (!previous) {
if (inst.mJobStatus.setContentTriggerConstraintSatisfied(true)) {
reportChange = true;
}
}
@ -255,50 +247,48 @@ public class ContentObserverController extends StateController {
}
@Override
public void dumpControllerState(PrintWriter pw) {
public void dumpControllerStateLocked(PrintWriter pw) {
pw.println("Content.");
synchronized (mLock) {
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
}
while (it.hasNext()) {
pw.print("," + String.valueOf(it.next().hashCode()));
}
pw.println();
int N = mObservers.size();
if (N > 0) {
pw.println("URIs:");
for (int i = 0; i < N; i++) {
ObserverInstance obs = mObservers.valueAt(i);
pw.print(" ");
pw.print(mObservers.keyAt(i));
pw.println(":");
pw.print(" ");
pw.println(obs);
pw.println(" Jobs:");
int M = obs.mJobs.size();
for (int j=0; j<M; j++) {
JobInstance inst = obs.mJobs.get(j);
pw.print(" ");
pw.print(inst.hashCode());
if (inst.mChangedAuthorities != null) {
pw.println(":");
pw.println(" Changed Authorities:");
for (int k=0; k<inst.mChangedAuthorities.size(); k++) {
pw.print(" ");
pw.println(inst.mChangedAuthorities.valueAt(k));
}
if (inst.mChangedUris != null) {
pw.println(" Changed URIs:");
for (int k = 0; k<inst.mChangedUris.size(); k++) {
pw.print(" ");
pw.println(inst.mChangedUris.valueAt(k));
}
}
} else {
pw.println();
Iterator<JobStatus> it = mTrackedTasks.iterator();
if (it.hasNext()) {
pw.print(String.valueOf(it.next().hashCode()));
}
while (it.hasNext()) {
pw.print("," + String.valueOf(it.next().hashCode()));
}
pw.println();
int N = mObservers.size();
if (N > 0) {
pw.println("URIs:");
for (int i = 0; i < N; i++) {
ObserverInstance obs = mObservers.valueAt(i);
pw.print(" ");
pw.print(mObservers.keyAt(i));
pw.println(":");
pw.print(" ");
pw.println(obs);
pw.println(" Jobs:");
int M = obs.mJobs.size();
for (int j=0; j<M; j++) {
JobInstance inst = obs.mJobs.get(j);
pw.print(" ");
pw.print(inst.hashCode());
if (inst.mChangedAuthorities != null) {
pw.println(":");
pw.println(" Changed Authorities:");
for (int k=0; k<inst.mChangedAuthorities.size(); k++) {
pw.print(" ");
pw.println(inst.mChangedAuthorities.valueAt(k));
}
if (inst.mChangedUris != null) {
pw.println(" Changed URIs:");
for (int k = 0; k<inst.mChangedUris.size(); k++) {
pw.print(" ");
pw.println(inst.mChangedUris.valueAt(k));
}
}
} else {
pw.println();
}
}
}

View File

@ -67,20 +67,16 @@ public class IdleController extends StateController {
* StateController interface
*/
@Override
public void maybeStartTrackingJob(JobStatus taskStatus, JobStatus lastJob) {
public void maybeStartTrackingJobLocked(JobStatus taskStatus, JobStatus lastJob) {
if (taskStatus.hasIdleConstraint()) {
synchronized (mLock) {
mTrackedTasks.add(taskStatus);
taskStatus.idleConstraintSatisfied.set(mIdleTracker.isIdle());
}
mTrackedTasks.add(taskStatus);
taskStatus.setIdleConstraintSatisfied(mIdleTracker.isIdle());
}
}
@Override
public void maybeStopTrackingJob(JobStatus taskStatus, boolean forUpdate) {
synchronized (mLock) {
mTrackedTasks.remove(taskStatus);
}
public void maybeStopTrackingJobLocked(JobStatus taskStatus, boolean forUpdate) {
mTrackedTasks.remove(taskStatus);
}
/**
@ -89,7 +85,7 @@ public class IdleController extends StateController {
void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
for (JobStatus task : mTrackedTasks) {
task.idleConstraintSatisfied.set(isIdle);
task.setIdleConstraintSatisfied(isIdle);
}
}
mStateChangedListener.onControllerStateChanged();
@ -194,17 +190,15 @@ public class IdleController extends StateController {
}
@Override
public void dumpControllerState(PrintWriter pw) {
synchronized (mLock) {
pw.print("Idle: ");
pw.println(mIdleTracker.isIdle() ? "true" : "false");
pw.println(mTrackedTasks.size());
for (int i = 0; i < mTrackedTasks.size(); i++) {
final JobStatus js = mTrackedTasks.get(i);
pw.print(" ");
pw.print(String.valueOf(js.hashCode()).substring(0, 3));
pw.println("..");
}
public void dumpControllerStateLocked(PrintWriter pw) {
pw.print("Idle: ");
pw.println(mIdleTracker.isIdle() ? "true" : "false");
pw.println(mTrackedTasks.size());
for (int i = 0; i < mTrackedTasks.size(); i++) {
final JobStatus js = mTrackedTasks.get(i);
pw.print(" ");
pw.print(String.valueOf(js.hashCode()).substring(0, 3));
pw.println("..");
}
}
}

View File

@ -42,15 +42,19 @@ import java.util.concurrent.atomic.AtomicBoolean;
* but we don't enforce that so this is safer.
* @hide
*/
public class JobStatus {
public final class JobStatus {
public static final long NO_LATEST_RUNTIME = Long.MAX_VALUE;
public static final long NO_EARLIEST_RUNTIME = 0L;
/**
* Service global lock. NOTE: this should be removed, having this class just rely on
* its callers doing the appropriate locking.
*/
final Object lock;
static final int CONSTRAINT_CHARGING = 1<<0;
static final int CONSTRAINT_TIMING_DELAY = 1<<1;
static final int CONSTRAINT_DEADLINE = 1<<2;
static final int CONSTRAINT_IDLE = 1<<3;
static final int CONSTRAINT_UNMETERED = 1<<4;
static final int CONSTRAINT_CONNECTIVITY = 1<<5;
static final int CONSTRAINT_APP_NOT_IDLE = 1<<6;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7;
final JobInfo job;
/** Uid of the package requesting this job. */
final int callingUid;
@ -61,15 +65,23 @@ public class JobStatus {
final int sourceUserId;
final int sourceUid;
/**
* Earliest point in the future at which this job will be eligible to run. A value of 0
* indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
*/
private final long earliestRunTimeElapsedMillis;
/**
* Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
* indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
*/
private final long latestRunTimeElapsedMillis;
/** How many times this job has failed, used to compute back-off. */
private final int numFailures;
// Constraints.
final AtomicBoolean chargingConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean timeDelayConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean deadlineConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean idleConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean unmeteredConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean connectivityConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean appNotIdleConstraintSatisfied = new AtomicBoolean();
final AtomicBoolean contentTriggerConstraintSatisfied = new AtomicBoolean();
final int requiredConstraints;
int satisfiedConstraints = 0;
// These are filled in by controllers when preparing for execution.
public ArraySet<Uri> changedUris;
@ -81,32 +93,18 @@ public class JobStatus {
*/
ContentObserverController.JobInstance contentObserverJobInstance;
/**
* Earliest point in the future at which this job will be eligible to run. A value of 0
* indicates there is no delay constraint. See {@link #hasTimingDelayConstraint()}.
*/
private long earliestRunTimeElapsedMillis;
/**
* Latest point in the future at which this job must be run. A value of {@link Long#MAX_VALUE}
* indicates there is no deadline constraint. See {@link #hasDeadlineConstraint()}.
*/
private long latestRunTimeElapsedMillis;
/** How many times this job has failed, used to compute back-off. */
private final int numFailures;
/** Provide a handle to the service that this job will be run on. */
public int getServiceToken() {
return callingUid;
}
private JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, int numFailures) {
this.lock = lock;
private JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, int numFailures, long earliestRunTimeElapsedMillis,
long latestRunTimeElapsedMillis) {
this.job = job;
this.callingUid = callingUid;
this.name = job.getService().flattenToShortString();
this.tag = "*job*/" + this.name;
this.numFailures = numFailures;
int tempSourceUid = -1;
if (sourceUserId != -1 && sourcePackageName != null) {
@ -126,40 +124,42 @@ public class JobStatus {
this.sourceUserId = sourceUserId;
this.sourcePackageName = sourcePackageName;
}
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this.numFailures = numFailures;
int requiredConstraints = 0;
if (job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY) {
requiredConstraints |= CONSTRAINT_CONNECTIVITY;
}
if (job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED) {
requiredConstraints |= CONSTRAINT_UNMETERED;
}
if (job.isRequireCharging()) {
requiredConstraints |= CONSTRAINT_CHARGING;
}
if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_TIMING_DELAY;
}
if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
requiredConstraints |= CONSTRAINT_DEADLINE;
}
if (job.isRequireDeviceIdle()) {
requiredConstraints |= CONSTRAINT_IDLE;
}
if (job.getTriggerContentUris() != null) {
requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
}
this.requiredConstraints = requiredConstraints;
}
/** Copy constructor. */
public JobStatus(JobStatus jobStatus) {
this(jobStatus.lock, jobStatus.getJob(), jobStatus.getUid(),
this(jobStatus.getJob(), jobStatus.getUid(),
jobStatus.getSourcePackageName(), jobStatus.getSourceUserId(),
jobStatus.getNumFailures());
this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime();
this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed();
}
/**
* Create a newly scheduled job.
* @param callingUid Uid of the package that scheduled this job.
* @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
* the calling package is the source.
* @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
* calling userId.
*/
public JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId) {
this(lock, job, callingUid, sourcePackageName, sourceUserId, 0);
final long elapsedNow = SystemClock.elapsedRealtime();
if (job.isPeriodic()) {
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
} else {
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
latestRunTimeElapsedMillis = job.hasLateConstraint() ?
elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
}
jobStatus.getNumFailures(), jobStatus.getEarliestRunTime(),
jobStatus.getLatestRunTimeElapsed());
}
/**
@ -169,23 +169,43 @@ public class JobStatus {
* wallclock runtime rather than resetting it on every boot.
* We consider a freshly loaded job to no longer be in back-off.
*/
public JobStatus(Object lock, JobInfo job, int callingUid, String sourcePackageName,
public JobStatus(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId, long earliestRunTimeElapsedMillis, long latestRunTimeElapsedMillis) {
this(lock, job, callingUid, sourcePackageName, sourceUserId, 0);
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
this.latestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
this(job, callingUid, sourcePackageName, sourceUserId, 0, earliestRunTimeElapsedMillis,
latestRunTimeElapsedMillis);
}
/** Create a new job to be rescheduled with the provided parameters. */
public JobStatus(JobStatus rescheduling, long newEarliestRuntimeElapsedMillis,
long newLatestRuntimeElapsedMillis, int backoffAttempt) {
this(rescheduling.lock, rescheduling.job, rescheduling.getUid(),
this(rescheduling.job, rescheduling.getUid(),
rescheduling.getSourcePackageName(),
rescheduling.getSourceUserId(), backoffAttempt);
rescheduling.getSourceUserId(), backoffAttempt, newEarliestRuntimeElapsedMillis,
newLatestRuntimeElapsedMillis);
}
earliestRunTimeElapsedMillis = newEarliestRuntimeElapsedMillis;
latestRunTimeElapsedMillis = newLatestRuntimeElapsedMillis;
/**
* Create a newly scheduled job.
* @param callingUid Uid of the package that scheduled this job.
* @param sourcePackageName Package name on whose behalf this job is scheduled. Null indicates
* the calling package is the source.
* @param sourceUserId User id for whom this job is scheduled. -1 indicates this is same as the
*/
public static JobStatus createFromJobInfo(JobInfo job, int callingUid, String sourcePackageName,
int sourceUserId) {
final long elapsedNow = SystemClock.elapsedRealtime();
final long earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis;
if (job.isPeriodic()) {
latestRunTimeElapsedMillis = elapsedNow + job.getIntervalMillis();
earliestRunTimeElapsedMillis = latestRunTimeElapsedMillis - job.getFlexMillis();
} else {
earliestRunTimeElapsedMillis = job.hasEarlyConstraint() ?
elapsedNow + job.getMinLatencyMillis() : NO_EARLIEST_RUNTIME;
latestRunTimeElapsedMillis = job.hasLateConstraint() ?
elapsedNow + job.getMaxExecutionDelayMillis() : NO_LATEST_RUNTIME;
}
return new JobStatus(job, callingUid, sourcePackageName, sourceUserId, 0,
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
}
public JobInfo getJob() {
@ -241,31 +261,31 @@ public class JobStatus {
}
public boolean hasConnectivityConstraint() {
return job.getNetworkType() == JobInfo.NETWORK_TYPE_ANY;
return (requiredConstraints&CONSTRAINT_CONNECTIVITY) != 0;
}
public boolean hasUnmeteredConstraint() {
return job.getNetworkType() == JobInfo.NETWORK_TYPE_UNMETERED;
return (requiredConstraints&CONSTRAINT_UNMETERED) != 0;
}
public boolean hasChargingConstraint() {
return job.isRequireCharging();
return (requiredConstraints&CONSTRAINT_CHARGING) != 0;
}
public boolean hasTimingDelayConstraint() {
return earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME;
return (requiredConstraints&CONSTRAINT_TIMING_DELAY) != 0;
}
public boolean hasDeadlineConstraint() {
return latestRunTimeElapsedMillis != NO_LATEST_RUNTIME;
return (requiredConstraints&CONSTRAINT_DEADLINE) != 0;
}
public boolean hasIdleConstraint() {
return job.isRequireDeviceIdle();
return (requiredConstraints&CONSTRAINT_IDLE) != 0;
}
public boolean hasContentTriggerConstraint() {
return job.getTriggerContentUris() != null;
return (requiredConstraints&CONSTRAINT_CONTENT_TRIGGER) != 0;
}
public boolean isPersisted() {
@ -280,35 +300,74 @@ public class JobStatus {
return latestRunTimeElapsedMillis;
}
boolean setChargingConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CHARGING, state);
}
boolean setTimingDelayConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_TIMING_DELAY, state);
}
boolean setDeadlineConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_DEADLINE, state);
}
boolean setIdleConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_IDLE, state);
}
boolean setUnmeteredConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_UNMETERED, state);
}
boolean setConnectivityConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONNECTIVITY, state);
}
boolean setAppNotIdleConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_APP_NOT_IDLE, state);
}
boolean setContentTriggerConstraintSatisfied(boolean state) {
return setConstraintSatisfied(CONSTRAINT_CONTENT_TRIGGER, state);
}
boolean setConstraintSatisfied(int constraint, boolean state) {
boolean old = (satisfiedConstraints&constraint) != 0;
if (old == state) {
return false;
}
satisfiedConstraints = (satisfiedConstraints&~constraint) | (state ? constraint : 0);
return true;
}
/**
* @return Whether or not this job is ready to run, based on its requirements. This is true if
* the constraints are satisfied <strong>or</strong> the deadline on the job has expired.
*/
public boolean isReady() {
synchronized (lock) {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
// (is an implementation detail. A periodic job should only run if it's constraints are
// satisfied).
// AppNotIdle implicit constraint trumps all!
return (isConstraintsSatisfied()
|| (!job.isPeriodic()
&& hasDeadlineConstraint() && deadlineConstraintSatisfied.get()))
&& appNotIdleConstraintSatisfied.get();
}
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
// (is an implementation detail. A periodic job should only run if it's constraints are
// satisfied).
// AppNotIdle implicit constraint trumps all!
return (isConstraintsSatisfied()
|| (!job.isPeriodic()
&& hasDeadlineConstraint() && (satisfiedConstraints&CONSTRAINT_DEADLINE) != 0))
&& (satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) != 0;
}
static final int CONSTRAINTS_OF_INTEREST =
CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY |
CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
/**
* @return Whether the constraints set on this job are satisfied.
*/
public boolean isConstraintsSatisfied() {
synchronized (lock) {
return (!hasChargingConstraint() || chargingConstraintSatisfied.get())
&& (!hasTimingDelayConstraint() || timeDelayConstraintSatisfied.get())
&& (!hasConnectivityConstraint() || connectivityConstraintSatisfied.get())
&& (!hasUnmeteredConstraint() || unmeteredConstraintSatisfied.get())
&& (!hasIdleConstraint() || idleConstraintSatisfied.get())
&& (!hasContentTriggerConstraint() || contentTriggerConstraintSatisfied.get());
}
final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
final int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
return (sat & req) == req;
}
public boolean matches(int uid, int jobId) {
@ -327,7 +386,7 @@ public class JobStatus {
+ ",I=" + job.isRequireDeviceIdle()
+ ",U=" + (job.getTriggerContentUris() != null)
+ ",F=" + numFailures + ",P=" + job.isPersisted()
+ ",ANI=" + appNotIdleConstraintSatisfied.get()
+ ",ANI=" + ((satisfiedConstraints&CONSTRAINT_APP_NOT_IDLE) != 0)
+ (isReady() ? "(READY)" : "")
+ "]";
}
@ -362,6 +421,33 @@ public class JobStatus {
return sb.toString();
}
void dumpConstraints(PrintWriter pw, int constraints) {
if ((constraints&CONSTRAINT_CHARGING) != 0) {
pw.print(" CHARGING");
}
if ((constraints&CONSTRAINT_TIMING_DELAY) != 0) {
pw.print(" TIMING_DELAY");
}
if ((constraints&CONSTRAINT_DEADLINE) != 0) {
pw.print(" DEADLINE");
}
if ((constraints&CONSTRAINT_IDLE) != 0) {
pw.print(" IDLE");
}
if ((constraints&CONSTRAINT_UNMETERED) != 0) {
pw.print(" UNMETERED");
}
if ((constraints&CONSTRAINT_CONNECTIVITY) != 0) {
pw.print(" CONNECTIVITY");
}
if ((constraints&CONSTRAINT_APP_NOT_IDLE) != 0) {
pw.print(" APP_NOT_IDLE");
}
if ((constraints&CONSTRAINT_CONTENT_TRIGGER) != 0) {
pw.print(" CONTENT_TRIGGER");
}
}
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix) {
pw.print(prefix); UserHandle.formatUid(pw, callingUid);
@ -426,39 +512,12 @@ public class JobStatus {
if (job.hasLateConstraint()) {
pw.print(prefix); pw.println(" Has late constraint");
}
pw.print(prefix); pw.println("Constraints:");
if (hasChargingConstraint()) {
pw.print(prefix); pw.print(" Charging: ");
pw.println(chargingConstraintSatisfied.get());
}
if (hasTimingDelayConstraint()) {
pw.print(prefix); pw.print(" Time delay: ");
pw.println(timeDelayConstraintSatisfied.get());
}
if (hasDeadlineConstraint()) {
pw.print(prefix); pw.print(" Deadline: ");
pw.println(deadlineConstraintSatisfied.get());
}
if (hasIdleConstraint()) {
pw.print(prefix); pw.print(" System idle: ");
pw.println(idleConstraintSatisfied.get());
}
if (hasUnmeteredConstraint()) {
pw.print(prefix); pw.print(" Unmetered: ");
pw.println(unmeteredConstraintSatisfied.get());
}
if (hasConnectivityConstraint()) {
pw.print(prefix); pw.print(" Connectivity: ");
pw.println(connectivityConstraintSatisfied.get());
}
if (hasIdleConstraint()) {
pw.print(prefix); pw.print(" App not idle: ");
pw.println(appNotIdleConstraintSatisfied.get());
}
if (hasContentTriggerConstraint()) {
pw.print(prefix); pw.print(" Content trigger: ");
pw.println(contentTriggerConstraintSatisfied.get());
}
pw.print(prefix); pw.print("Required constraints:");
dumpConstraints(pw, requiredConstraints);
pw.println();
pw.print(prefix); pw.print("Satisfied constraints:");
dumpConstraints(pw, satisfiedConstraints);
pw.println();
if (changedAuthorities != null) {
pw.print(prefix); pw.println("Changed authorities:");
for (int i=0; i<changedAuthorities.size(); i++) {

View File

@ -52,21 +52,21 @@ public abstract class StateController {
* Also called when updating a task, so implementing controllers have to be aware of
* preexisting tasks.
*/
public abstract void maybeStartTrackingJob(JobStatus jobStatus, JobStatus lastJob);
public abstract void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob);
/**
* Optionally implement logic here to prepare the job to be executed.
*/
public void prepareForExecution(JobStatus jobStatus) {
public void prepareForExecutionLocked(JobStatus jobStatus) {
}
/**
* Remove task - this will happen if the task is cancelled, completed, etc.
*/
public abstract void maybeStopTrackingJob(JobStatus jobStatus, boolean forUpdate);
public abstract void maybeStopTrackingJobLocked(JobStatus jobStatus, boolean forUpdate);
/**
* Called when a new job is being created to reschedule an old failed job.
*/
public void rescheduleForFailure(JobStatus newJob, JobStatus failureToReschedule) {
}
public abstract void dumpControllerState(PrintWriter pw);
public abstract void dumpControllerStateLocked(PrintWriter pw);
}

View File

@ -72,28 +72,26 @@ public class TimeController extends StateController {
* list.
*/
@Override
public void maybeStartTrackingJob(JobStatus job, JobStatus lastJob) {
synchronized (mLock) {
if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
maybeStopTrackingJob(job, false);
boolean isInsert = false;
ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
while (it.hasPrevious()) {
JobStatus ts = it.previous();
if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
// Insert
isInsert = true;
break;
}
public void maybeStartTrackingJobLocked(JobStatus job, JobStatus lastJob) {
if (job.hasTimingDelayConstraint() || job.hasDeadlineConstraint()) {
maybeStopTrackingJobLocked(job, false);
boolean isInsert = false;
ListIterator<JobStatus> it = mTrackedJobs.listIterator(mTrackedJobs.size());
while (it.hasPrevious()) {
JobStatus ts = it.previous();
if (ts.getLatestRunTimeElapsed() < job.getLatestRunTimeElapsed()) {
// Insert
isInsert = true;
break;
}
if (isInsert) {
it.next();
}
it.add(job);
maybeUpdateAlarms(
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
}
if (isInsert) {
it.next();
}
it.add(job);
maybeUpdateAlarmsLocked(
job.hasTimingDelayConstraint() ? job.getEarliestRunTime() : Long.MAX_VALUE,
job.hasDeadlineConstraint() ? job.getLatestRunTimeElapsed() : Long.MAX_VALUE);
}
}
@ -103,12 +101,10 @@ public class TimeController extends StateController {
* Really an == comparison should be enough, but why play with fate? We'll do <=.
*/
@Override
public void maybeStopTrackingJob(JobStatus job, boolean forUpdate) {
synchronized (mLock) {
if (mTrackedJobs.remove(job)) {
checkExpiredDelaysAndResetAlarm();
checkExpiredDeadlinesAndResetAlarm();
}
public void maybeStopTrackingJobLocked(JobStatus job, boolean forUpdate) {
if (mTrackedJobs.remove(job)) {
checkExpiredDelaysAndResetAlarm();
checkExpiredDeadlinesAndResetAlarm();
}
}
@ -118,14 +114,14 @@ public class TimeController extends StateController {
* the job's deadline is fulfilled - unlike other controllers a time constraint can't toggle
* back and forth.
*/
private boolean canStopTrackingJob(JobStatus job) {
private boolean canStopTrackingJobLocked(JobStatus job) {
return (!job.hasTimingDelayConstraint() ||
job.timeDelayConstraintSatisfied.get()) &&
(job.satisfiedConstraints&JobStatus.CONSTRAINT_TIMING_DELAY) != 0) &&
(!job.hasDeadlineConstraint() ||
job.deadlineConstraintSatisfied.get());
(job.satisfiedConstraints&JobStatus.CONSTRAINT_DEADLINE) != 0);
}
private void ensureAlarmService() {
private void ensureAlarmServiceLocked() {
if (mAlarmService == null) {
mAlarmService = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
}
@ -149,7 +145,7 @@ public class TimeController extends StateController {
final long jobDeadline = job.getLatestRunTimeElapsed();
if (jobDeadline <= nowElapsedMillis) {
job.deadlineConstraintSatisfied.set(true);
job.setDeadlineConstraintSatisfied(true);
mStateChangedListener.onRunJobNow(job);
it.remove();
} else { // Sorted by expiry time, so take the next one and stop.
@ -157,7 +153,7 @@ public class TimeController extends StateController {
break;
}
}
setDeadlineExpiredAlarm(nextExpiryTime);
setDeadlineExpiredAlarmLocked(nextExpiryTime);
}
}
@ -178,8 +174,8 @@ public class TimeController extends StateController {
}
final long jobDelayTime = job.getEarliestRunTime();
if (jobDelayTime <= nowElapsedMillis) {
job.timeDelayConstraintSatisfied.set(true);
if (canStopTrackingJob(job)) {
job.setTimingDelayConstraintSatisfied(true);
if (canStopTrackingJobLocked(job)) {
it.remove();
}
if (job.isReady()) {
@ -194,16 +190,16 @@ public class TimeController extends StateController {
if (ready) {
mStateChangedListener.onControllerStateChanged();
}
setDelayExpiredAlarm(nextDelayTime);
setDelayExpiredAlarmLocked(nextDelayTime);
}
}
private void maybeUpdateAlarms(long delayExpiredElapsed, long deadlineExpiredElapsed) {
private void maybeUpdateAlarmsLocked(long delayExpiredElapsed, long deadlineExpiredElapsed) {
if (delayExpiredElapsed < mNextDelayExpiredElapsedMillis) {
setDelayExpiredAlarm(delayExpiredElapsed);
setDelayExpiredAlarmLocked(delayExpiredElapsed);
}
if (deadlineExpiredElapsed < mNextJobExpiredElapsedMillis) {
setDeadlineExpiredAlarm(deadlineExpiredElapsed);
setDeadlineExpiredAlarmLocked(deadlineExpiredElapsed);
}
}
@ -212,10 +208,11 @@ public class TimeController extends StateController {
* delay will expire.
* This alarm <b>will</b> wake up the phone.
*/
private void setDelayExpiredAlarm(long alarmTimeElapsedMillis) {
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListener(DELAY_TAG, mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis);
updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
mNextDelayExpiredElapsedMillis);
}
/**
@ -223,10 +220,11 @@ public class TimeController extends StateController {
* deadline will expire.
* This alarm <b>will</b> wake up the phone.
*/
private void setDeadlineExpiredAlarm(long alarmTimeElapsedMillis) {
private void setDeadlineExpiredAlarmLocked(long alarmTimeElapsedMillis) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
updateAlarmWithListener(DEADLINE_TAG, mDeadlineExpiredListener, mNextJobExpiredElapsedMillis);
updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
mNextJobExpiredElapsedMillis);
}
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
@ -237,9 +235,9 @@ public class TimeController extends StateController {
return proposedAlarmTimeElapsedMillis;
}
private void updateAlarmWithListener(String tag, OnAlarmListener listener,
private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
long alarmTimeElapsed) {
ensureAlarmService();
ensureAlarmServiceLocked();
if (alarmTimeElapsed == Long.MAX_VALUE) {
mAlarmService.cancel(listener);
} else {
@ -274,7 +272,7 @@ public class TimeController extends StateController {
};
@Override
public void dumpControllerState(PrintWriter pw) {
public void dumpControllerStateLocked(PrintWriter pw) {
final long nowElapsed = SystemClock.elapsedRealtime();
pw.println("Alarms (" + SystemClock.elapsedRealtime() + ")");
pw.println(

View File

@ -58,7 +58,7 @@ public class JobStoreTest extends AndroidTestCase {
.setMinimumLatency(runFromMillis)
.setPersisted(true)
.build();
final JobStatus ts = new JobStatus(this, task, SOME_UID, null, -1);
final JobStatus ts = JobStatus.createFromJobInfo(task, SOME_UID, null, -1);
mTaskStoreUnderTest.add(ts);
Thread.sleep(IO_WAIT);
// Manually load tasks from xml file.
@ -91,8 +91,8 @@ public class JobStoreTest extends AndroidTestCase {
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.setPersisted(true)
.build();
final JobStatus taskStatus1 = new JobStatus(this, task1, SOME_UID, null, -1);
final JobStatus taskStatus2 = new JobStatus(this, task2, SOME_UID, null, -1);
final JobStatus taskStatus1 = JobStatus.createFromJobInfo(task1, SOME_UID, null, -1);
final JobStatus taskStatus2 = JobStatus.createFromJobInfo(task2, SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus1);
mTaskStoreUnderTest.add(taskStatus2);
Thread.sleep(IO_WAIT);
@ -140,7 +140,7 @@ public class JobStoreTest extends AndroidTestCase {
extras.putInt("into", 3);
b.setExtras(extras);
final JobInfo task = b.build();
JobStatus taskStatus = new JobStatus(this, task, SOME_UID, null, -1);
JobStatus taskStatus = JobStatus.createFromJobInfo(task, SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
@ -157,7 +157,7 @@ public class JobStoreTest extends AndroidTestCase {
.setPeriodic(10000L)
.setRequiresCharging(true)
.setPersisted(true);
JobStatus taskStatus = new JobStatus(this, b.build(), SOME_UID,
JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID,
"com.google.android.gms", 0);
mTaskStoreUnderTest.add(taskStatus);
@ -179,7 +179,7 @@ public class JobStoreTest extends AndroidTestCase {
.setPeriodic(5*60*60*1000, 1*60*60*1000)
.setRequiresCharging(true)
.setPersisted(true);
JobStatus taskStatus = new JobStatus(this, b.build(), SOME_UID, null, -1);
JobStatus taskStatus = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(taskStatus);
Thread.sleep(IO_WAIT);
@ -204,7 +204,7 @@ public class JobStoreTest extends AndroidTestCase {
SystemClock.elapsedRealtime() + (TWO_HOURS * ONE_HOUR) + TWO_HOURS; // > period+flex
final long invalidEarlyRuntimeElapsedMillis =
invalidLateRuntimeElapsedMillis - TWO_HOURS; // Early is (late - period).
final JobStatus js = new JobStatus(this, b.build(), SOME_UID, "somePackage",
final JobStatus js = new JobStatus(b.build(), SOME_UID, "somePackage",
0 /* sourceUserId */,
invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis);
@ -231,7 +231,7 @@ public class JobStoreTest extends AndroidTestCase {
.setOverrideDeadline(5000)
.setPriority(42)
.setPersisted(true);
final JobStatus js = new JobStatus(this, b.build(), SOME_UID, null, -1);
final JobStatus js = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(js);
Thread.sleep(IO_WAIT);
final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();
@ -247,12 +247,12 @@ public class JobStoreTest extends AndroidTestCase {
JobInfo.Builder b = new Builder(42, mComponent)
.setOverrideDeadline(10000)
.setPersisted(false);
JobStatus jsNonPersisted = new JobStatus(this, b.build(), SOME_UID, null, -1);
JobStatus jsNonPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(jsNonPersisted);
b = new Builder(43, mComponent)
.setOverrideDeadline(10000)
.setPersisted(true);
JobStatus jsPersisted = new JobStatus(this, b.build(), SOME_UID, null, -1);
JobStatus jsPersisted = JobStatus.createFromJobInfo(b.build(), SOME_UID, null, -1);
mTaskStoreUnderTest.add(jsPersisted);
Thread.sleep(IO_WAIT);
final ArraySet<JobStatus> jobStatusSet = new ArraySet<JobStatus>();