• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.job;
18 
19 import android.app.ActivityManager;
20 import android.app.AppGlobals;
21 import android.content.pm.IPackageManager;
22 import android.content.pm.PackageManager;
23 import android.os.BasicShellCommandHandler;
24 import android.os.Binder;
25 import android.os.UserHandle;
26 
27 import java.io.PrintWriter;
28 
29 public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
30     public static final int CMD_ERR_NO_PACKAGE = -1000;
31     public static final int CMD_ERR_NO_JOB = -1001;
32     public static final int CMD_ERR_CONSTRAINTS = -1002;
33 
34     JobSchedulerService mInternal;
35     IPackageManager mPM;
36 
JobSchedulerShellCommand(JobSchedulerService service)37     JobSchedulerShellCommand(JobSchedulerService service) {
38         mInternal = service;
39         mPM = AppGlobals.getPackageManager();
40     }
41 
42     @Override
onCommand(String cmd)43     public int onCommand(String cmd) {
44         final PrintWriter pw = getOutPrintWriter();
45         try {
46             switch (cmd != null ? cmd : "") {
47                 case "run":
48                     return runJob(pw);
49                 case "timeout":
50                     return timeout(pw);
51                 case "cancel":
52                     return cancelJob(pw);
53                 case "monitor-battery":
54                     return monitorBattery(pw);
55                 case "get-battery-seq":
56                     return getBatterySeq(pw);
57                 case "get-battery-charging":
58                     return getBatteryCharging(pw);
59                 case "get-battery-not-low":
60                     return getBatteryNotLow(pw);
61                 case "get-storage-seq":
62                     return getStorageSeq(pw);
63                 case "get-storage-not-low":
64                     return getStorageNotLow(pw);
65                 case "get-job-state":
66                     return getJobState(pw);
67                 case "heartbeat":
68                     return doHeartbeat(pw);
69                 case "reset-execution-quota":
70                     return resetExecutionQuota(pw);
71                 case "reset-schedule-quota":
72                     return resetScheduleQuota(pw);
73                 case "trigger-dock-state":
74                     return triggerDockState(pw);
75                 default:
76                     return handleDefaultCommands(cmd);
77             }
78         } catch (Exception e) {
79             pw.println("Exception: " + e);
80         }
81         return -1;
82     }
83 
checkPermission(String operation)84     private void checkPermission(String operation) throws Exception {
85         final int uid = Binder.getCallingUid();
86         if (uid == 0) {
87             // Root can do anything.
88             return;
89         }
90         final int perm = mPM.checkUidPermission(
91                 "android.permission.CHANGE_APP_IDLE_STATE", uid);
92         if (perm != PackageManager.PERMISSION_GRANTED) {
93             throw new SecurityException("Uid " + uid
94                     + " not permitted to " + operation);
95         }
96     }
97 
printError(int errCode, String pkgName, int userId, int jobId)98     private boolean printError(int errCode, String pkgName, int userId, int jobId) {
99         PrintWriter pw;
100         switch (errCode) {
101             case CMD_ERR_NO_PACKAGE:
102                 pw = getErrPrintWriter();
103                 pw.print("Package not found: ");
104                 pw.print(pkgName);
105                 pw.print(" / user ");
106                 pw.println(userId);
107                 return true;
108 
109             case CMD_ERR_NO_JOB:
110                 pw = getErrPrintWriter();
111                 pw.print("Could not find job ");
112                 pw.print(jobId);
113                 pw.print(" in package ");
114                 pw.print(pkgName);
115                 pw.print(" / user ");
116                 pw.println(userId);
117                 return true;
118 
119             case CMD_ERR_CONSTRAINTS:
120                 pw = getErrPrintWriter();
121                 pw.print("Job ");
122                 pw.print(jobId);
123                 pw.print(" in package ");
124                 pw.print(pkgName);
125                 pw.print(" / user ");
126                 pw.print(userId);
127                 pw.println(" has functional constraints but --force not specified");
128                 return true;
129 
130             default:
131                 return false;
132         }
133     }
134 
runJob(PrintWriter pw)135     private int runJob(PrintWriter pw) throws Exception {
136         checkPermission("force scheduled jobs");
137 
138         boolean force = false;
139         boolean satisfied = false;
140         int userId = UserHandle.USER_SYSTEM;
141 
142         String opt;
143         while ((opt = getNextOption()) != null) {
144             switch (opt) {
145                 case "-f":
146                 case "--force":
147                     force = true;
148                     break;
149 
150                 case "-s":
151                 case "--satisfied":
152                     satisfied = true;
153                     break;
154 
155                 case "-u":
156                 case "--user":
157                     userId = Integer.parseInt(getNextArgRequired());
158                     break;
159 
160                 default:
161                     pw.println("Error: unknown option '" + opt + "'");
162                     return -1;
163             }
164         }
165 
166         if (force && satisfied) {
167             pw.println("Cannot specify both --force and --satisfied");
168             return -1;
169         }
170 
171         final String pkgName = getNextArgRequired();
172         final int jobId = Integer.parseInt(getNextArgRequired());
173 
174         final long ident = Binder.clearCallingIdentity();
175         try {
176             int ret = mInternal.executeRunCommand(pkgName, userId, jobId, satisfied, force);
177             if (printError(ret, pkgName, userId, jobId)) {
178                 return ret;
179             }
180 
181             // success!
182             pw.print("Running job");
183             if (force) {
184                 pw.print(" [FORCED]");
185             }
186             pw.println();
187 
188             return ret;
189         } finally {
190             Binder.restoreCallingIdentity(ident);
191         }
192     }
193 
timeout(PrintWriter pw)194     private int timeout(PrintWriter pw) throws Exception {
195         checkPermission("force timeout jobs");
196 
197         int userId = UserHandle.USER_ALL;
198 
199         String opt;
200         while ((opt = getNextOption()) != null) {
201             switch (opt) {
202                 case "-u":
203                 case "--user":
204                     userId = UserHandle.parseUserArg(getNextArgRequired());
205                     break;
206 
207                 default:
208                     pw.println("Error: unknown option '" + opt + "'");
209                     return -1;
210             }
211         }
212 
213         if (userId == UserHandle.USER_CURRENT) {
214             userId = ActivityManager.getCurrentUser();
215         }
216 
217         final String pkgName = getNextArg();
218         final String jobIdStr = getNextArg();
219         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
220 
221         final long ident = Binder.clearCallingIdentity();
222         try {
223             return mInternal.executeTimeoutCommand(pw, pkgName, userId, jobIdStr != null, jobId);
224         } finally {
225             Binder.restoreCallingIdentity(ident);
226         }
227     }
228 
cancelJob(PrintWriter pw)229     private int cancelJob(PrintWriter pw) throws Exception {
230         checkPermission("cancel jobs");
231 
232         int userId = UserHandle.USER_SYSTEM;
233 
234         String opt;
235         while ((opt = getNextOption()) != null) {
236             switch (opt) {
237                 case "-u":
238                 case "--user":
239                     userId = UserHandle.parseUserArg(getNextArgRequired());
240                     break;
241 
242                 default:
243                     pw.println("Error: unknown option '" + opt + "'");
244                     return -1;
245             }
246         }
247 
248         if (userId < 0) {
249             pw.println("Error: must specify a concrete user ID");
250             return -1;
251         }
252 
253         final String pkgName = getNextArg();
254         final String jobIdStr = getNextArg();
255         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
256 
257         final long ident = Binder.clearCallingIdentity();
258         try {
259             return mInternal.executeCancelCommand(pw, pkgName, userId, jobIdStr != null, jobId);
260         } finally {
261             Binder.restoreCallingIdentity(ident);
262         }
263     }
264 
monitorBattery(PrintWriter pw)265     private int monitorBattery(PrintWriter pw) throws Exception {
266         checkPermission("change battery monitoring");
267         String opt = getNextArgRequired();
268         boolean enabled;
269         if ("on".equals(opt)) {
270             enabled = true;
271         } else if ("off".equals(opt)) {
272             enabled = false;
273         } else {
274             getErrPrintWriter().println("Error: unknown option " + opt);
275             return 1;
276         }
277         final long ident = Binder.clearCallingIdentity();
278         try {
279             mInternal.setMonitorBattery(enabled);
280             if (enabled) pw.println("Battery monitoring enabled");
281             else pw.println("Battery monitoring disabled");
282         } finally {
283             Binder.restoreCallingIdentity(ident);
284         }
285         return 0;
286     }
287 
getBatterySeq(PrintWriter pw)288     private int getBatterySeq(PrintWriter pw) {
289         int seq = mInternal.getBatterySeq();
290         pw.println(seq);
291         return 0;
292     }
293 
getBatteryCharging(PrintWriter pw)294     private int getBatteryCharging(PrintWriter pw) {
295         boolean val = mInternal.getBatteryCharging();
296         pw.println(val);
297         return 0;
298     }
299 
getBatteryNotLow(PrintWriter pw)300     private int getBatteryNotLow(PrintWriter pw) {
301         boolean val = mInternal.getBatteryNotLow();
302         pw.println(val);
303         return 0;
304     }
305 
getStorageSeq(PrintWriter pw)306     private int getStorageSeq(PrintWriter pw) {
307         int seq = mInternal.getStorageSeq();
308         pw.println(seq);
309         return 0;
310     }
311 
getStorageNotLow(PrintWriter pw)312     private int getStorageNotLow(PrintWriter pw) {
313         boolean val = mInternal.getStorageNotLow();
314         pw.println(val);
315         return 0;
316     }
317 
getJobState(PrintWriter pw)318     private int getJobState(PrintWriter pw) throws Exception {
319         checkPermission("force timeout jobs");
320 
321         int userId = UserHandle.USER_SYSTEM;
322 
323         String opt;
324         while ((opt = getNextOption()) != null) {
325             switch (opt) {
326                 case "-u":
327                 case "--user":
328                     userId = UserHandle.parseUserArg(getNextArgRequired());
329                     break;
330 
331                 default:
332                     pw.println("Error: unknown option '" + opt + "'");
333                     return -1;
334             }
335         }
336 
337         if (userId == UserHandle.USER_CURRENT) {
338             userId = ActivityManager.getCurrentUser();
339         }
340 
341         final String pkgName = getNextArgRequired();
342         final String jobIdStr = getNextArgRequired();
343         final int jobId = Integer.parseInt(jobIdStr);
344 
345         final long ident = Binder.clearCallingIdentity();
346         try {
347             int ret = mInternal.getJobState(pw, pkgName, userId, jobId);
348             printError(ret, pkgName, userId, jobId);
349             return ret;
350         } finally {
351             Binder.restoreCallingIdentity(ident);
352         }
353     }
354 
doHeartbeat(PrintWriter pw)355     private int doHeartbeat(PrintWriter pw) throws Exception {
356         checkPermission("manipulate scheduler heartbeat");
357 
358         pw.println("Heartbeat command is no longer supported");
359         return -1;
360     }
361 
resetExecutionQuota(PrintWriter pw)362     private int resetExecutionQuota(PrintWriter pw) throws Exception {
363         checkPermission("reset execution quota");
364 
365         int userId = UserHandle.USER_SYSTEM;
366 
367         String opt;
368         while ((opt = getNextOption()) != null) {
369             switch (opt) {
370                 case "-u":
371                 case "--user":
372                     userId = UserHandle.parseUserArg(getNextArgRequired());
373                     break;
374 
375                 default:
376                     pw.println("Error: unknown option '" + opt + "'");
377                     return -1;
378             }
379         }
380 
381         if (userId == UserHandle.USER_CURRENT) {
382             userId = ActivityManager.getCurrentUser();
383         }
384 
385         final String pkgName = getNextArgRequired();
386 
387         final long ident = Binder.clearCallingIdentity();
388         try {
389             mInternal.resetExecutionQuota(pkgName, userId);
390         } finally {
391             Binder.restoreCallingIdentity(ident);
392         }
393         return 0;
394     }
395 
resetScheduleQuota(PrintWriter pw)396     private int resetScheduleQuota(PrintWriter pw) throws Exception {
397         checkPermission("reset schedule quota");
398 
399         final long ident = Binder.clearCallingIdentity();
400         try {
401             mInternal.resetScheduleQuota();
402         } finally {
403             Binder.restoreCallingIdentity(ident);
404         }
405         return 0;
406     }
407 
triggerDockState(PrintWriter pw)408     private int triggerDockState(PrintWriter pw) throws Exception {
409         checkPermission("trigger wireless charging dock state");
410 
411         final String opt = getNextArgRequired();
412         boolean idleState;
413         if ("idle".equals(opt)) {
414             idleState = true;
415         } else if ("active".equals(opt)) {
416             idleState = false;
417         } else {
418             getErrPrintWriter().println("Error: unknown option " + opt);
419             return 1;
420         }
421 
422         final long ident = Binder.clearCallingIdentity();
423         try {
424             mInternal.triggerDockState(idleState);
425         } finally {
426             Binder.restoreCallingIdentity(ident);
427         }
428         return 0;
429     }
430 
431     @Override
onHelp()432     public void onHelp() {
433         final PrintWriter pw = getOutPrintWriter();
434 
435         pw.println("Job scheduler (jobscheduler) commands:");
436         pw.println("  help");
437         pw.println("    Print this help text.");
438         pw.println("  run [-f | --force] [-s | --satisfied] [-u | --user USER_ID] PACKAGE JOB_ID");
439         pw.println("    Trigger immediate execution of a specific scheduled job. For historical");
440         pw.println("    reasons, some constraints, such as battery, are ignored when this");
441         pw.println("    command is called. If you don't want any constraints to be ignored,");
442         pw.println("    include the -s flag.");
443         pw.println("    Options:");
444         pw.println("      -f or --force: run the job even if technical constraints such as");
445         pw.println("         connectivity are not currently met. This is incompatible with -f ");
446         pw.println("         and so an error will be reported if both are given.");
447         pw.println("      -s or --satisfied: run the job only if all constraints are met.");
448         pw.println("         This is incompatible with -f and so an error will be reported");
449         pw.println("         if both are given.");
450         pw.println("      -u or --user: specify which user's job is to be run; the default is");
451         pw.println("         the primary or system user");
452         pw.println("  timeout [-u | --user USER_ID] [PACKAGE] [JOB_ID]");
453         pw.println("    Trigger immediate timeout of currently executing jobs, as if their.");
454         pw.println("    execution timeout had expired.");
455         pw.println("    Options:");
456         pw.println("      -u or --user: specify which user's job is to be run; the default is");
457         pw.println("         all users");
458         pw.println("  cancel [-u | --user USER_ID] PACKAGE [JOB_ID]");
459         pw.println("    Cancel a scheduled job.  If a job ID is not supplied, all jobs scheduled");
460         pw.println("    by that package will be canceled.  USE WITH CAUTION.");
461         pw.println("    Options:");
462         pw.println("      -u or --user: specify which user's job is to be run; the default is");
463         pw.println("         the primary or system user");
464         pw.println("  heartbeat [num]");
465         pw.println("    No longer used.");
466         pw.println("  monitor-battery [on|off]");
467         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
468         pw.println("    on makes get-battery-seq useful.");
469         pw.println("  get-battery-seq");
470         pw.println("    Return the last battery update sequence number that was received.");
471         pw.println("  get-battery-charging");
472         pw.println("    Return whether the battery is currently considered to be charging.");
473         pw.println("  get-battery-not-low");
474         pw.println("    Return whether the battery is currently considered to not be low.");
475         pw.println("  get-storage-seq");
476         pw.println("    Return the last storage update sequence number that was received.");
477         pw.println("  get-storage-not-low");
478         pw.println("    Return whether storage is currently considered to not be low.");
479         pw.println("  get-job-state [-u | --user USER_ID] PACKAGE JOB_ID");
480         pw.println("    Return the current state of a job, may be any combination of:");
481         pw.println("      pending: currently on the pending list, waiting to be active");
482         pw.println("      active: job is actively running");
483         pw.println("      user-stopped: job can't run because its user is stopped");
484         pw.println("      backing-up: job can't run because app is currently backing up its data");
485         pw.println("      no-component: job can't run because its component is not available");
486         pw.println("      ready: job is ready to run (all constraints satisfied or bypassed)");
487         pw.println("      waiting: if nothing else above is printed, job not ready to run");
488         pw.println("    Options:");
489         pw.println("      -u or --user: specify which user's job is to be run; the default is");
490         pw.println("         the primary or system user");
491         pw.println("  trigger-dock-state [idle|active]");
492         pw.println("    Trigger wireless charging dock state.  Active by default.");
493         pw.println();
494     }
495 
496 }
497