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/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