diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index 936e2055f67cd..b7af5445cae48 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -434,6 +434,12 @@ public class JobInfo implements Parcelable { * @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 7fe192c0b472b..ca7022d310412 100644 --- a/core/java/android/app/job/JobScheduler.java +++ b/core/java/android/app/job/JobScheduler.java @@ -22,7 +22,13 @@ import android.content.Context; /** * 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. *

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 14457ec9c04c2..6771ccece996d 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 @@ public class JobSchedulerService extends com.android.server.SystemService 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 90c203ab646b0..97dfad39fa7ff 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 @@ public interface StateChangedListener { /** * 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. null + * indicates to the scheduler that any ready jobs should be flushed. */ 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 538a2520311b8..309e034de7ba0 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 @@ public class BatteryController extends StateController { @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 @@ public class BatteryController extends StateController { } } } + // 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 @@ public class BatteryController extends StateController { } // 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 @@ public class BatteryController extends StateController { }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 @@ public class BatteryController extends StateController { } } } + + 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