Loading services/core/java/com/android/server/job/JobSchedulerService.java +42 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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++) { Loading services/core/java/com/android/server/job/JobSchedulerShellCommand.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * 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(); } } services/core/java/com/android/server/job/controllers/JobStatus.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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() Loading @@ -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; } Loading Loading
services/core/java/com/android/server/job/JobSchedulerService.java +42 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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++) { Loading
services/core/java/com/android/server/job/JobSchedulerShellCommand.java 0 → 100644 +154 −0 Original line number Diff line number Diff line /* * 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(); } }
services/core/java/com/android/server/job/controllers/JobStatus.java +25 −2 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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. Loading Loading @@ -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() Loading @@ -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; } Loading