Merge "Add 'shell cmd jobscheduler run ....' to run a job immediately" into nyc-dev

This commit is contained in:
Chris Tate
2016-03-09 01:31:26 +00:00
committed by Android (Google) Code Review
3 changed files with 221 additions and 3 deletions

View File

@@ -47,14 +47,15 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
import com.android.internal.app.ProcessStats;
@@ -1337,8 +1338,48 @@ public final class JobSchedulerService extends com.android.server.SystemService
Binder.restoreCallingIdentity(identityToken);
}
}
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ResultReceiver resultReceiver) throws RemoteException {
(new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
this, in, out, err, args, resultReceiver);
}
};
// Shell command infrastructure: run the given job immediately
int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
if (DEBUG) {
Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
+ " " + jobId + " f=" + force);
}
try {
final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
if (uid < 0) {
return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
}
synchronized (mLock) {
final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
if (js == null) {
return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
}
js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
if (!js.isConstraintsSatisfied()) {
js.overrideState = 0;
return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
}
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
} 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++) {

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.server.job;
import android.app.AppGlobals;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.RemoteException;
import android.os.ShellCommand;
import android.os.UserHandle;
import java.io.PrintWriter;
public class JobSchedulerShellCommand extends ShellCommand {
public static final int CMD_ERR_NO_PACKAGE = -1000;
public static final int CMD_ERR_NO_JOB = -1001;
public static final int CMD_ERR_CONSTRAINTS = -1002;
JobSchedulerService mInternal;
IPackageManager mPM;
JobSchedulerShellCommand(JobSchedulerService service) {
mInternal = service;
mPM = AppGlobals.getPackageManager();
}
@Override
public int onCommand(String cmd) {
final PrintWriter pw = getOutPrintWriter();
try {
if ("run".equals(cmd)) {
return runJob();
} else {
return handleDefaultCommands(cmd);
}
} catch (Exception e) {
pw.println("Exception: " + e);
}
return -1;
}
private int runJob() {
try {
final int uid = Binder.getCallingUid();
final int perm = mPM.checkUidPermission(
"android.permission.CHANGE_APP_IDLE_STATE", uid);
if (perm != PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Uid " + uid
+ " not permitted to force scheduled jobs");
}
} catch (RemoteException e) {
// Can't happen
}
final PrintWriter pw = getOutPrintWriter();
boolean force = false;
int userId = UserHandle.USER_SYSTEM;
String opt;
while ((opt = getNextOption()) != null) {
switch (opt) {
case "-f":
case "--force":
force = true;
break;
case "-u":
case "--user":
userId = Integer.parseInt(getNextArgRequired());
break;
default:
pw.println("Error: unknown option '" + opt + "'");
return -1;
}
}
final String pkgName = getNextArgRequired();
final int jobId = Integer.parseInt(getNextArgRequired());
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;
}
return ret;
}
@Override
public void onHelp() {
final PrintWriter pw = getOutPrintWriter();
pw.println("Job scheduler (jobscheduler) commands:");
pw.println(" help");
pw.println(" Print this help text.");
pw.println();
pw.println(" run [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID");
pw.println(" Trigger immediate execution of a specific scheduled job.");
pw.println(" Options:");
pw.println(" -f or --force: run the job even if technical constraints such as");
pw.println(" connectivity are not currently met");
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

@@ -55,6 +55,11 @@ public final class JobStatus {
static final int CONSTRAINT_APP_NOT_IDLE = 1<<6;
static final int CONSTRAINT_CONTENT_TRIGGER = 1<<7;
// Soft override: ignore constraints like time that don't affect API availability
public static final int OVERRIDE_SOFT = 1;
// Full override: ignore all constraints including API-affecting like connectivity
public static final int OVERRIDE_FULL = 2;
final JobInfo job;
/** Uid of the package requesting this job. */
final int callingUid;
@@ -91,6 +96,9 @@ public final class JobStatus {
public int lastEvaluatedPriority;
// Used by shell commands
public int overrideState = 0;
/**
* For use only by ContentObserverController: state it is maintaining about content URIs
* being observed.
@@ -370,7 +378,7 @@ public final class JobStatus {
*/
public boolean isReady() {
// Deadline constraint trumps other constraints (except for periodic jobs where deadline
// (is an implementation detail. A periodic job should only run if it's constraints are
// is an implementation detail. A periodic job should only run if its constraints are
// satisfied).
// AppNotIdle implicit constraint trumps all!
return (isConstraintsSatisfied()
@@ -384,12 +392,27 @@ public final class JobStatus {
CONSTRAINT_CONNECTIVITY | CONSTRAINT_UNMETERED |
CONSTRAINT_IDLE | CONSTRAINT_CONTENT_TRIGGER;
// Soft override covers all non-"functional" constraints
static final int SOFT_OVERRIDE_CONSTRAINTS =
CONSTRAINT_CHARGING | CONSTRAINT_TIMING_DELAY | CONSTRAINT_IDLE;
/**
* @return Whether the constraints set on this job are satisfied.
*/
public boolean isConstraintsSatisfied() {
if (overrideState == OVERRIDE_FULL) {
// force override: the job is always runnable
return true;
}
final int req = requiredConstraints & CONSTRAINTS_OF_INTEREST;
final int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
int sat = satisfiedConstraints & CONSTRAINTS_OF_INTEREST;
if (overrideState == OVERRIDE_SOFT) {
// override: pretend all 'soft' requirements are satisfied
sat |= (requiredConstraints & SOFT_OVERRIDE_CONSTRAINTS);
}
return (sat & req) == req;
}