diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index c08f866086568..0e55c3f19c3d5 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -1270,6 +1270,17 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + private void stopNonReadyActiveJobsLocked() { + for (int i=0; i(); } newReadyJobs.add(job); - } else if (areJobConstraintsNotSatisfiedLocked(job)) { - stopJobOnServiceContextLocked(job, - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); } } @@ -1387,9 +1396,6 @@ public final class JobSchedulerService extends com.android.server.SystemService runnableJobs = new ArrayList<>(); } runnableJobs.add(job); - } else if (areJobConstraintsNotSatisfiedLocked(job)) { - stopJobOnServiceContextLocked(job, - JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED); } } @@ -1439,6 +1445,7 @@ public final class JobSchedulerService extends com.android.server.SystemService noteJobsNonpending(mPendingJobs); mPendingJobs.clear(); + stopNonReadyActiveJobsLocked(); mJobs.forEachJob(mMaybeQueueFunctor); mMaybeQueueFunctor.postProcess(); } @@ -1515,15 +1522,6 @@ public final class JobSchedulerService extends com.android.server.SystemService 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 @@ -2088,6 +2086,83 @@ public final class JobSchedulerService extends com.android.server.SystemService } } + int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) { + try { + final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, + userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM); + if (uid < 0) { + pw.print("unknown("); pw.print(pkgName); pw.println(")"); + return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE; + } + + synchronized (mLock) { + final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId); + if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js); + if (js == null) { + pw.print("unknown("); UserHandle.formatUid(pw, uid); + pw.print("/jid"); pw.print(jobId); pw.println(")"); + return JobSchedulerShellCommand.CMD_ERR_NO_JOB; + } + + boolean printed = false; + if (mPendingJobs.contains(js)) { + pw.print("pending"); + printed = true; + } + if (isCurrentlyActiveLocked(js)) { + if (printed) { + pw.print(" "); + } + printed = true; + pw.println("active"); + } + if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) { + if (printed) { + pw.print(" "); + } + printed = true; + pw.println("user-stopped"); + } + if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) { + if (printed) { + pw.print(" "); + } + printed = true; + pw.println("backing-up"); + } + boolean componentPresent = false; + try { + componentPresent = (AppGlobals.getPackageManager().getServiceInfo( + js.getServiceComponent(), + PackageManager.MATCH_DEBUG_TRIAGED_MISSING, + js.getUserId()) != null); + } catch (RemoteException e) { + } + if (!componentPresent) { + if (printed) { + pw.print(" "); + } + printed = true; + pw.println("no-component"); + } + if (js.isReady()) { + if (printed) { + pw.print(" "); + } + printed = true; + pw.println("ready"); + } + if (!printed) { + pw.print("waiting"); + } + pw.println(); + } + } catch (RemoteException e) { + // can't happen + } + return 0; + } + private String printContextIdToJobMap(JobStatus[] map, String initial) { StringBuilder s = new StringBuilder(initial + ": "); for (int i=0; i= 0; i--) { - final JobStatus ts = mTrackedTasks.valueAt(i); - boolean previous = ts.setChargingConstraintSatisfied(stablePower); - if (previous != stablePower) { - reportChange = true; - } - previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow); - if (previous != batteryNotLow) { - reportChange = true; - } + for (int i = mTrackedTasks.size() - 1; i >= 0; i--) { + final JobStatus ts = mTrackedTasks.valueAt(i); + boolean previous = ts.setChargingConstraintSatisfied(stablePower); + if (previous != stablePower) { + reportChange = true; + } + previous = ts.setBatteryNotLowConstraintSatisfied(batteryNotLow); + if (previous != batteryNotLow) { + reportChange = true; } } - // 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 || batteryNotLow) { + // If one of our conditions has been satisfied, always schedule any newly ready jobs. mStateChangedListener.onRunJobNow(null); + } else if (reportChange) { + // Otherwise, just let the job scheduler know the state has changed and take care of it + // as it thinks is best. + mStateChangedListener.onControllerStateChanged(); } } @@ -201,38 +198,42 @@ public class BatteryController extends StateController { @VisibleForTesting public void onReceiveInternal(Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_BATTERY_LOW.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Battery life too low to do work. @ " - + SystemClock.elapsedRealtime()); + synchronized (mLock) { + final String action = intent.getAction(); + if (Intent.ACTION_BATTERY_LOW.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Battery life too low to do work. @ " + + SystemClock.elapsedRealtime()); + } + // If we get this action, the battery is discharging => it isn't plugged in so + // there's no work to cancel. We track this variable for the case where it is + // charging, but hasn't been for long enough to be healthy. + mBatteryHealthy = false; + maybeReportNewChargingStateLocked(); + } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Battery life healthy enough to do work. @ " + + SystemClock.elapsedRealtime()); + } + mBatteryHealthy = true; + maybeReportNewChargingStateLocked(); + } else if (BatteryManager.ACTION_CHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Received charging intent, fired @ " + + SystemClock.elapsedRealtime()); + } + mCharging = true; + maybeReportNewChargingStateLocked(); + } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { + if (DEBUG) { + Slog.d(TAG, "Disconnected from power."); + } + mCharging = false; + maybeReportNewChargingStateLocked(); } - // If we get this action, the battery is discharging => it isn't plugged in so - // there's no work to cancel. We track this variable for the case where it is - // charging, but hasn't been for long enough to be healthy. - mBatteryHealthy = false; - } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Battery life healthy enough to do work. @ " - + SystemClock.elapsedRealtime()); - } - mBatteryHealthy = true; - maybeReportNewChargingState(); - } else if (BatteryManager.ACTION_CHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Received charging intent, fired @ " - + SystemClock.elapsedRealtime()); - } - mCharging = true; - maybeReportNewChargingState(); - } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) { - if (DEBUG) { - Slog.d(TAG, "Disconnected from power."); - } - mCharging = false; - maybeReportNewChargingState(); + mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, + mLastBatterySeq); } - mLastBatterySeq = intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq); } }