Merge "Add 'shell cmd jobscheduler run ....' to run a job immediately" into nyc-dev
This commit is contained in:
@@ -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++) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user