Merge "Fix issue #38244875: Bring back testChargingConstraintFails" into oc-dev

This commit is contained in:
Dianne Hackborn
2017-05-17 00:50:43 +00:00
committed by Android (Google) Code Review
3 changed files with 247 additions and 107 deletions

View File

@@ -1270,6 +1270,17 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
}
private void stopNonReadyActiveJobsLocked() {
for (int i=0; i<mActiveServices.size(); i++) {
JobServiceContext serviceContext = mActiveServices.get(i);
final JobStatus running = serviceContext.getRunningJobLocked();
if (running != null && !running.isReady()) {
serviceContext.cancelExecutingJobLocked(
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
}
}
}
/**
* Run through list of jobs and execute all possible - at least one is expired so we do
* as many as we can.
@@ -1280,6 +1291,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
noteJobsNonpending(mPendingJobs);
mPendingJobs.clear();
stopNonReadyActiveJobsLocked();
mJobs.forEachJob(mReadyQueueFunctor);
mReadyQueueFunctor.postProcess();
@@ -1306,9 +1318,6 @@ public final class JobSchedulerService extends com.android.server.SystemService
newReadyJobs = new ArrayList<JobStatus>();
}
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<map.length; i++) {
@@ -2152,7 +2227,8 @@ public final class JobSchedulerService extends com.android.server.SystemService
}
final int filterUidFinal = UserHandle.getAppId(filterUid);
final long now = SystemClock.elapsedRealtime();
final long nowElapsed = SystemClock.elapsedRealtime();
final long nowUptime = SystemClock.uptimeMillis();
synchronized (mLock) {
mConstants.dump(pw);
pw.println();
@@ -2184,7 +2260,7 @@ public final class JobSchedulerService extends com.android.server.SystemService
continue;
}
job.dump(pw, " ", true, now);
job.dump(pw, " ", true, nowElapsed);
pw.print(" Ready: ");
pw.print(isReadyToBeExecutedLocked(job));
pw.print(" (job=");
@@ -2254,14 +2330,14 @@ public final class JobSchedulerService extends com.android.server.SystemService
JobStatus job = mPendingJobs.get(i);
pw.print(" Pending #"); pw.print(i); pw.print(": ");
pw.println(job.toShortString());
job.dump(pw, " ", false, now);
job.dump(pw, " ", false, nowElapsed);
int priority = evaluateJobPriorityLocked(job);
if (priority != JobInfo.PRIORITY_DEFAULT) {
pw.print(" Evaluated priority: "); pw.println(priority);
}
pw.print(" Tag: "); pw.println(job.getTag());
pw.print(" Enq: ");
TimeUtils.formatDuration(job.madePending - now, pw);
TimeUtils.formatDuration(job.madePending - nowUptime, pw);
pw.println();
}
pw.println();
@@ -2276,17 +2352,17 @@ public final class JobSchedulerService extends com.android.server.SystemService
} else {
pw.println(job.toShortString());
pw.print(" Running for: ");
TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
pw.print(", timeout at: ");
TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
pw.println();
job.dump(pw, " ", false, now);
job.dump(pw, " ", false, nowElapsed);
int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
if (priority != JobInfo.PRIORITY_DEFAULT) {
pw.print(" Evaluated priority: "); pw.println(priority);
}
pw.print(" Active at ");
TimeUtils.formatDuration(job.madeActive - now, pw);
TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
pw.print(", pending for ");
TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
pw.println();

View File

@@ -60,6 +60,8 @@ public class JobSchedulerShellCommand extends ShellCommand {
return getStorageSeq(pw);
case "get-storage-not-low":
return getStorageNotLow(pw);
case "get-job-state":
return getJobState(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -83,6 +85,43 @@ public class JobSchedulerShellCommand extends ShellCommand {
}
}
private boolean printError(int errCode, String pkgName, int userId, int jobId) {
PrintWriter pw;
switch (errCode) {
case CMD_ERR_NO_PACKAGE:
pw = getErrPrintWriter();
pw.print("Package not found: ");
pw.print(pkgName);
pw.print(" / user ");
pw.println(userId);
return true;
case CMD_ERR_NO_JOB:
pw = getErrPrintWriter();
pw.print("Could not find job ");
pw.print(jobId);
pw.print(" in package ");
pw.print(pkgName);
pw.print(" / user ");
pw.println(userId);
return true;
case CMD_ERR_CONSTRAINTS:
pw = getErrPrintWriter();
pw.print("Job ");
pw.print(jobId);
pw.print(" in package ");
pw.print(pkgName);
pw.print(" / user ");
pw.print(userId);
pw.println(" has functional constraints but --force not specified");
return true;
default:
return false;
}
}
private int runJob(PrintWriter pw) throws Exception {
checkPermission("force scheduled jobs");
@@ -114,42 +153,17 @@ public class JobSchedulerShellCommand extends ShellCommand {
final long ident = Binder.clearCallingIdentity();
try {
int ret = mInternal.executeRunCommand(pkgName, userId, jobId, force);
switch (ret) {
case CMD_ERR_NO_PACKAGE:
pw.print("Package not found: ");
pw.print(pkgName);
pw.print(" / user ");
pw.println(userId);
break;
case CMD_ERR_NO_JOB:
pw.print("Could not find job ");
pw.print(jobId);
pw.print(" in package ");
pw.print(pkgName);
pw.print(" / user ");
pw.println(userId);
break;
case CMD_ERR_CONSTRAINTS:
pw.print("Job ");
pw.print(jobId);
pw.print(" in package ");
pw.print(pkgName);
pw.print(" / user ");
pw.print(userId);
pw.println(" has functional constraints but --force not specified");
break;
default:
// success!
pw.print("Running job");
if (force) {
pw.print(" [FORCED]");
}
pw.println();
break;
if (printError(ret, pkgName, userId, jobId)) {
return ret;
}
// success!
pw.print("Running job");
if (force) {
pw.print(" [FORCED]");
}
pw.println();
return ret;
} finally {
Binder.restoreCallingIdentity(ident);
@@ -244,6 +258,43 @@ public class JobSchedulerShellCommand extends ShellCommand {
return 0;
}
private int getJobState(PrintWriter pw) throws Exception {
checkPermission("force timeout jobs");
int userId = UserHandle.USER_SYSTEM;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "-u":
case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired());
break;
default:
pw.println("Error: unknown option '" + opt + "'");
return -1;
}
}
if (userId == UserHandle.USER_CURRENT) {
userId = ActivityManager.getCurrentUser();
}
final String pkgName = getNextArgRequired();
final String jobIdStr = getNextArgRequired();
final int jobId = Integer.parseInt(jobIdStr);
final long ident = Binder.clearCallingIdentity();
try {
int ret = mInternal.getJobState(pw, pkgName, userId, jobId);
printError(ret, pkgName, userId, jobId);
return ret;
} finally {
Binder.restoreCallingIdentity(ident);
}
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
@@ -277,6 +328,18 @@ public class JobSchedulerShellCommand extends ShellCommand {
pw.println(" Return the last storage update sequence number that was received.");
pw.println(" get-storage-not-low");
pw.println(" Return whether storage is currently considered to not be low.");
pw.println(" get-job-state [-u | --user USER_ID] PACKAGE JOB_ID");
pw.println(" Return the current state of a job, may be any combination of:");
pw.println(" pending: currently on the pending list, waiting to be active");
pw.println(" active: job is actively running");
pw.println(" user-stopped: job can't run because its user is stopped");
pw.println(" backing-up: job can't run because app is currently backing up its data");
pw.println(" no-component: job can't run because its component is not available");
pw.println(" ready: job is ready to run (all constraints satisfied or bypassed)");
pw.println(" waiting: if nothing else above is printed, job not ready to run");
pw.println(" Options:");
pw.println(" -u or --user: specify which user's job is to be run; the default is");
pw.println(" the primary or system user");
pw.println();
}

View File

@@ -93,34 +93,31 @@ public class BatteryController extends StateController {
}
}
private void maybeReportNewChargingState() {
private void maybeReportNewChargingStateLocked() {
final boolean stablePower = mChargeTracker.isOnStablePower();
final boolean batteryNotLow = mChargeTracker.isBatteryNotLow();
if (DEBUG) {
Slog.d(TAG, "maybeReportNewChargingState: " + stablePower);
Slog.d(TAG, "maybeReportNewChargingStateLocked: " + stablePower);
}
boolean reportChange = false;
synchronized (mLock) {
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;
}
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);
}
}