JobScheduler needs to flush queue on charging
Also make it illegal to schedule a job with no constraints.
BUG: 16877705
Change-Id: Iae57286bc4f73163a7e3c9d2d531623fd50f1f72
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 936e205..b7af544 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -434,6 +434,12 @@
* @return The job object to hand to the JobScheduler. This object is immutable.
*/
public JobInfo build() {
+ // Allow tasks with no constraints. What am I, a database?
+ if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging &&
+ !mRequiresDeviceIdle && mNetworkCapabilities == NetworkType.NONE) {
+ throw new IllegalArgumentException("You're trying to build a job with no " +
+ "constraints, this is not allowed.");
+ }
mExtras = new PersistableBundle(mExtras); // Make our own copy.
// Check that a deadline was not set on a periodic job.
if (mIsPeriodic && (mMaxExecutionDelayMillis != 0L)) {
diff --git a/core/java/android/app/job/JobScheduler.java b/core/java/android/app/job/JobScheduler.java
index 7fe192c..ca7022d 100644
--- a/core/java/android/app/job/JobScheduler.java
+++ b/core/java/android/app/job/JobScheduler.java
@@ -22,7 +22,13 @@
/**
* Class for scheduling various types of jobs with the scheduling framework on the device.
- *
+ * See {@link android.app.job.JobInfo} for more description of the types of jobs that can be run
+ * and how to construct them.
+ * The framework will be intelligent about when you receive your callbacks, and attempt to batch
+ * and defer them as much as possible. Typically if you don't specify a deadline on your job, it
+ * can be run at any moment depending on the current state of the JobScheduler's internal queue,
+ * however it might be deferred as long as until the next time the device is connected to a power
+ * source.
* <p>You do not
* instantiate this class directly; instead, retrieve it through
* {@link android.content.Context#getSystemService
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 14457ec..6771cce 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -162,6 +162,7 @@
JobStatus jobStatus = new JobStatus(job, uId);
cancelJob(uId, job.getId());
startTrackingJob(jobStatus);
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
return JobScheduler.RESULT_SUCCESS;
}
@@ -507,7 +508,9 @@
case MSG_JOB_EXPIRED:
synchronized (mJobs) {
JobStatus runNow = (JobStatus) message.obj;
- if (!mPendingJobs.contains(runNow)) {
+ // 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 && !mPendingJobs.contains(runNow)) {
mPendingJobs.add(runNow);
}
}
diff --git a/services/core/java/com/android/server/job/StateChangedListener.java b/services/core/java/com/android/server/job/StateChangedListener.java
index 90c203a..97dfad3 100644
--- a/services/core/java/com/android/server/job/StateChangedListener.java
+++ b/services/core/java/com/android/server/job/StateChangedListener.java
@@ -33,7 +33,8 @@
/**
* Called by the controller to notify the JobManager that regardless of the state of the task,
* it must be run immediately.
- * @param jobStatus The state of the task which is to be run immediately.
+ * @param jobStatus The state of the task which is to be run immediately. <strong>null
+ * indicates to the scheduler that any ready jobs should be flushed.</strong>
*/
public void onRunJobNow(JobStatus jobStatus);
}
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 538a252..309e034 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -84,15 +84,15 @@
@Override
public void maybeStartTrackingJob(JobStatus taskStatus) {
+ final boolean isOnStablePower = mChargeTracker.isOnStablePower();
if (taskStatus.hasChargingConstraint()) {
- final boolean isOnStablePower = mChargeTracker.isOnStablePower();
synchronized (mTrackedTasks) {
mTrackedTasks.add(taskStatus);
taskStatus.chargingConstraintSatisfied.set(isOnStablePower);
}
- if (isOnStablePower) {
- mStateChangedListener.onControllerStateChanged();
- }
+ }
+ if (isOnStablePower) {
+ mChargeTracker.setStableChargingAlarm();
}
}
@@ -119,9 +119,15 @@
}
}
}
+ // Let the scheduler know that state has changed. This may or may not result in an
+ // execution.
if (reportChange) {
mStateChangedListener.onControllerStateChanged();
}
+ // Also tell the scheduler that any ready jobs should be flushed.
+ if (stablePower) {
+ mStateChangedListener.onRunJobNow(null);
+ }
}
public class ChargingTracker extends BroadcastReceiver {
@@ -196,9 +202,7 @@
}
// Set up an alarm for ACTION_CHARGING_STABLE - we don't want to kick off tasks
// here if the user unplugs the phone immediately.
- mAlarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + STABLE_CHARGING_THRESHOLD_MILLIS,
- mStableChargingTriggerIntent);
+ setStableChargingAlarm();
mCharging = true;
} else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
if (DEBUG) {
@@ -211,7 +215,7 @@
}else if (ACTION_CHARGING_STABLE.equals(action)) {
// Here's where we actually do the notify for a task being ready.
if (DEBUG) {
- Slog.d(TAG, "Battery connected fired @ " + SystemClock.elapsedRealtime()
+ Slog.d(TAG, "Stable charging fired @ " + SystemClock.elapsedRealtime()
+ " charging: " + mCharging);
}
if (mCharging) { // Should never receive this intent if mCharging is false.
@@ -219,6 +223,17 @@
}
}
}
+
+ void setStableChargingAlarm() {
+ final long alarmTriggerElapsed =
+ SystemClock.elapsedRealtime() + STABLE_CHARGING_THRESHOLD_MILLIS;
+ if (DEBUG) {
+ Slog.d(TAG, "Setting stable alarm to go off in " +
+ (STABLE_CHARGING_THRESHOLD_MILLIS / 1000) + "s");
+ }
+ mAlarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTriggerElapsed,
+ mStableChargingTriggerIntent);
+ }
}
@Override