• 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.Manifest;
20 import android.annotation.Nullable;
21 import android.app.ActivityManager;
22 import android.app.AppGlobals;
23 import android.app.job.JobParameters;
24 import android.content.pm.IPackageManager;
25 import android.content.pm.PackageManager;
26 import android.os.Binder;
27 import android.os.UserHandle;
28 
29 import com.android.modules.utils.BasicShellCommandHandler;
30 import com.android.server.job.controllers.JobStatus;
31 
32 import java.io.PrintWriter;
33 
34 public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
35     public static final int CMD_ERR_NO_PACKAGE = -1000;
36     public static final int CMD_ERR_NO_JOB = -1001;
37     public static final int CMD_ERR_CONSTRAINTS = -1002;
38 
39     static final int BYTE_OPTION_DOWNLOAD = 0;
40     static final int BYTE_OPTION_UPLOAD = 1;
41 
42     JobSchedulerService mInternal;
43     IPackageManager mPM;
44 
JobSchedulerShellCommand(JobSchedulerService service)45     JobSchedulerShellCommand(JobSchedulerService service) {
46         mInternal = service;
47         mPM = AppGlobals.getPackageManager();
48     }
49 
50     @Override
onCommand(String cmd)51     public int onCommand(String cmd) {
52         final PrintWriter pw = getOutPrintWriter();
53         try {
54             switch (cmd != null ? cmd : "") {
55                 case "run":
56                     return runJob(pw);
57                 case "timeout":
58                     return timeout(pw);
59                 case "cancel":
60                     return cancelJob(pw);
61                 case "monitor-battery":
62                     return monitorBattery(pw);
63                 case "disable-flex-policy":
64                     return disableFlexPolicy(pw);
65                 case "enable-flex-policy":
66                     return enableFlexPolicy(pw);
67                 case "get-aconfig-flag-state":
68                     return getAconfigFlagState(pw);
69                 case "get-battery-seq":
70                     return getBatterySeq(pw);
71                 case "get-battery-charging":
72                     return getBatteryCharging(pw);
73                 case "get-battery-not-low":
74                     return getBatteryNotLow(pw);
75                 case "get-config-value":
76                     return getConfigValue(pw);
77                 case "get-estimated-download-bytes":
78                     return getEstimatedNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
79                 case "get-estimated-upload-bytes":
80                     return getEstimatedNetworkBytes(pw, BYTE_OPTION_UPLOAD);
81                 case "get-storage-seq":
82                     return getStorageSeq(pw);
83                 case "get-storage-not-low":
84                     return getStorageNotLow(pw);
85                 case "get-transferred-download-bytes":
86                     return getTransferredNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
87                 case "get-transferred-upload-bytes":
88                     return getTransferredNetworkBytes(pw, BYTE_OPTION_UPLOAD);
89                 case "get-job-wakelock-tag":
90                     return getJobWakelockTag(pw);
91                 case "get-job-state":
92                     return getJobState(pw);
93                 case "heartbeat":
94                     return doHeartbeat(pw);
95                 case "cache-config-changes":
96                     return cacheConfigChanges(pw);
97                 case "reset-execution-quota":
98                     return resetExecutionQuota(pw);
99                 case "reset-schedule-quota":
100                     return resetScheduleQuota(pw);
101                 case "reset-flex-policy":
102                     return resetFlexPolicy(pw);
103                 case "stop":
104                     return stop(pw);
105                 case "trigger-dock-state":
106                     return triggerDockState(pw);
107                 default:
108                     return handleDefaultCommands(cmd);
109             }
110         } catch (Exception e) {
111             pw.println("Exception: " + e);
112         }
113         return -1;
114     }
115 
checkPermission(String operation)116     private void checkPermission(String operation) throws Exception {
117         checkPermission(operation, Manifest.permission.CHANGE_APP_IDLE_STATE);
118     }
119 
checkPermission(String operation, String permission)120     private void checkPermission(String operation, String permission) throws Exception {
121         final int uid = Binder.getCallingUid();
122         if (uid == 0) {
123             // Root can do anything.
124             return;
125         }
126         final int perm = mPM.checkUidPermission(permission, uid);
127         if (perm != PackageManager.PERMISSION_GRANTED) {
128             throw new SecurityException("Uid " + uid
129                     + " not permitted to " + operation);
130         }
131     }
132 
printError(int errCode, String pkgName, int userId, @Nullable String namespace, int jobId)133     private boolean printError(int errCode, String pkgName, int userId, @Nullable String namespace,
134             int jobId) {
135         PrintWriter pw;
136         switch (errCode) {
137             case CMD_ERR_NO_PACKAGE:
138                 pw = getErrPrintWriter();
139                 pw.print("Package not found: ");
140                 pw.print(pkgName);
141                 pw.print(" / user ");
142                 pw.println(userId);
143                 return true;
144 
145             case CMD_ERR_NO_JOB:
146                 pw = getErrPrintWriter();
147                 pw.print("Could not find job ");
148                 pw.print(jobId);
149                 pw.print(" in package ");
150                 pw.print(pkgName);
151                 if (namespace != null) {
152                     pw.print(" / namespace ");
153                     pw.print(namespace);
154                 }
155                 pw.print(" / user ");
156                 pw.println(userId);
157                 return true;
158 
159             case CMD_ERR_CONSTRAINTS:
160                 pw = getErrPrintWriter();
161                 pw.print("Job ");
162                 pw.print(jobId);
163                 pw.print(" in package ");
164                 pw.print(pkgName);
165                 if (namespace != null) {
166                     pw.print(" / namespace ");
167                     pw.print(namespace);
168                 }
169                 pw.print(" / user ");
170                 pw.print(userId);
171                 pw.println(" has functional constraints but --force not specified");
172                 return true;
173 
174             default:
175                 return false;
176         }
177     }
178 
runJob(PrintWriter pw)179     private int runJob(PrintWriter pw) throws Exception {
180         checkPermission("force scheduled jobs");
181 
182         boolean force = false;
183         boolean satisfied = false;
184         int userId = UserHandle.USER_SYSTEM;
185         String namespace = null;
186 
187         String opt;
188         while ((opt = getNextOption()) != null) {
189             switch (opt) {
190                 case "-f":
191                 case "--force":
192                     force = true;
193                     break;
194 
195                 case "-s":
196                 case "--satisfied":
197                     satisfied = true;
198                     break;
199 
200                 case "-u":
201                 case "--user":
202                     userId = UserHandle.parseUserArg(getNextArgRequired());
203                     break;
204 
205                 case "-n":
206                 case "--namespace":
207                     namespace = getNextArgRequired();
208                     break;
209 
210                 default:
211                     pw.println("Error: unknown option '" + opt + "'");
212                     return -1;
213             }
214         }
215 
216         if (force && satisfied) {
217             pw.println("Cannot specify both --force and --satisfied");
218             return -1;
219         }
220 
221         if (userId == UserHandle.USER_CURRENT) {
222             userId = ActivityManager.getCurrentUser();
223         }
224 
225         final String pkgName = getNextArgRequired();
226         final int jobId = Integer.parseInt(getNextArgRequired());
227 
228         final long ident = Binder.clearCallingIdentity();
229         try {
230             int ret = mInternal.executeRunCommand(pkgName, userId, namespace,
231                     jobId, satisfied, force);
232             if (printError(ret, pkgName, userId, namespace, jobId)) {
233                 return ret;
234             }
235 
236             // success!
237             pw.print("Running job");
238             if (force) {
239                 pw.print(" [FORCED]");
240             }
241             pw.println();
242 
243             return ret;
244         } finally {
245             Binder.restoreCallingIdentity(ident);
246         }
247     }
248 
timeout(PrintWriter pw)249     private int timeout(PrintWriter pw) throws Exception {
250         checkPermission("force timeout jobs");
251 
252         int userId = UserHandle.USER_ALL;
253         String namespace = null;
254 
255         String opt;
256         while ((opt = getNextOption()) != null) {
257             switch (opt) {
258                 case "-u":
259                 case "--user":
260                     userId = UserHandle.parseUserArg(getNextArgRequired());
261                     break;
262 
263                 case "-n":
264                 case "--namespace":
265                     namespace = getNextArgRequired();
266                     break;
267 
268                 default:
269                     pw.println("Error: unknown option '" + opt + "'");
270                     return -1;
271             }
272         }
273 
274         if (userId == UserHandle.USER_CURRENT) {
275             userId = ActivityManager.getCurrentUser();
276         }
277 
278         final String pkgName = getNextArg();
279         final String jobIdStr = getNextArg();
280         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
281 
282         final long ident = Binder.clearCallingIdentity();
283         try {
284             return mInternal.executeStopCommand(pw, pkgName, userId, namespace,
285                     jobIdStr != null, jobId,
286                     JobParameters.STOP_REASON_TIMEOUT, JobParameters.INTERNAL_STOP_REASON_TIMEOUT);
287         } finally {
288             Binder.restoreCallingIdentity(ident);
289         }
290     }
291 
cancelJob(PrintWriter pw)292     private int cancelJob(PrintWriter pw) throws Exception {
293         checkPermission("cancel jobs");
294 
295         int userId = UserHandle.USER_SYSTEM;
296         String namespace = null;
297 
298         String opt;
299         while ((opt = getNextOption()) != null) {
300             switch (opt) {
301                 case "-u":
302                 case "--user":
303                     userId = UserHandle.parseUserArg(getNextArgRequired());
304                     break;
305 
306                 case "-n":
307                 case "--namespace":
308                     namespace = getNextArgRequired();
309                     break;
310 
311                 default:
312                     pw.println("Error: unknown option '" + opt + "'");
313                     return -1;
314             }
315         }
316 
317         if (userId < 0) {
318             pw.println("Error: must specify a concrete user ID");
319             return -1;
320         }
321 
322         final String pkgName = getNextArg();
323         final String jobIdStr = getNextArg();
324         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
325 
326         final long ident = Binder.clearCallingIdentity();
327         try {
328             return mInternal.executeCancelCommand(pw, pkgName, userId, namespace,
329                     jobIdStr != null, jobId);
330         } finally {
331             Binder.restoreCallingIdentity(ident);
332         }
333     }
334 
monitorBattery(PrintWriter pw)335     private int monitorBattery(PrintWriter pw) throws Exception {
336         checkPermission("change battery monitoring");
337         String opt = getNextArgRequired();
338         boolean enabled;
339         if ("on".equals(opt)) {
340             enabled = true;
341         } else if ("off".equals(opt)) {
342             enabled = false;
343         } else {
344             getErrPrintWriter().println("Error: unknown option " + opt);
345             return 1;
346         }
347         final long ident = Binder.clearCallingIdentity();
348         try {
349             mInternal.setMonitorBattery(enabled);
350             if (enabled) pw.println("Battery monitoring enabled");
351             else pw.println("Battery monitoring disabled");
352         } finally {
353             Binder.restoreCallingIdentity(ident);
354         }
355         return 0;
356     }
357 
disableFlexPolicy(PrintWriter pw)358     private int disableFlexPolicy(PrintWriter pw) throws Exception {
359         checkPermission("disable flex policy");
360 
361         final long ident = Binder.clearCallingIdentity();
362         try {
363             mInternal.setFlexPolicy(true, 0);
364             pw.println("Set flex policy to 0");
365             return 0;
366         } finally {
367             Binder.restoreCallingIdentity(ident);
368         }
369     }
370 
enableFlexPolicy(PrintWriter pw)371     private int enableFlexPolicy(PrintWriter pw) throws Exception {
372         checkPermission("enable flex policy");
373 
374         int enabled = 0;
375 
376         String opt;
377         while ((opt = getNextOption()) != null) {
378             switch (opt) {
379                 case "-o":
380                 case "--option":
381                     final String constraint = getNextArgRequired();
382                     switch (constraint) {
383                         case "battery-not-low":
384                             enabled |= JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
385                             break;
386                         case "charging":
387                             enabled |= JobStatus.CONSTRAINT_CHARGING;
388                             break;
389                         case "connectivity":
390                             enabled |= JobStatus.CONSTRAINT_CONNECTIVITY;
391                             break;
392                         case "idle":
393                             enabled |= JobStatus.CONSTRAINT_IDLE;
394                             break;
395                         default:
396                             pw.println("Unsupported option: " + constraint);
397                             return -1;
398                     }
399                     break;
400 
401                 default:
402                     pw.println("Error: unknown option '" + opt + "'");
403                     return -1;
404             }
405         }
406 
407         final long ident = Binder.clearCallingIdentity();
408         try {
409             mInternal.setFlexPolicy(true, enabled);
410             pw.println("Set flex policy to " + enabled);
411             return 0;
412         } finally {
413             Binder.restoreCallingIdentity(ident);
414         }
415     }
416 
getAconfigFlagState(PrintWriter pw)417     private int getAconfigFlagState(PrintWriter pw) throws Exception {
418         checkPermission("get aconfig flag state", Manifest.permission.DUMP);
419 
420         final String flagName = getNextArgRequired();
421 
422         switch (flagName) {
423             case android.app.job.Flags.FLAG_ENFORCE_MINIMUM_TIME_WINDOWS:
424                 pw.println(android.app.job.Flags.enforceMinimumTimeWindows());
425                 break;
426             case android.app.job.Flags.FLAG_JOB_DEBUG_INFO_APIS:
427                 pw.println(android.app.job.Flags.jobDebugInfoApis());
428                 break;
429             case android.app.job.Flags.FLAG_ADD_TYPE_INFO_TO_WAKELOCK_TAG:
430                 pw.println(android.app.job.Flags.addTypeInfoToWakelockTag());
431                 break;
432             case com.android.server.job.Flags.FLAG_BATCH_ACTIVE_BUCKET_JOBS:
433                 pw.println(com.android.server.job.Flags.batchActiveBucketJobs());
434                 break;
435             case com.android.server.job.Flags.FLAG_BATCH_CONNECTIVITY_JOBS_PER_NETWORK:
436                 pw.println(com.android.server.job.Flags.batchConnectivityJobsPerNetwork());
437                 break;
438             case com.android.server.job.Flags.FLAG_DO_NOT_FORCE_RUSH_EXECUTION_AT_BOOT:
439                 pw.println(com.android.server.job.Flags.doNotForceRushExecutionAtBoot());
440                 break;
441             case android.app.job.Flags.FLAG_IGNORE_IMPORTANT_WHILE_FOREGROUND:
442                 pw.println(android.app.job.Flags.ignoreImportantWhileForeground());
443                 break;
444             case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_API:
445                 pw.println(android.app.job.Flags.getPendingJobReasonsApi());
446                 break;
447             case android.app.job.Flags.FLAG_GET_PENDING_JOB_REASONS_HISTORY_API:
448                 pw.println(android.app.job.Flags.getPendingJobReasonsHistoryApi());
449                 break;
450             default:
451                 pw.println("Unknown flag: " + flagName);
452                 break;
453         }
454         return 0;
455     }
456 
getBatterySeq(PrintWriter pw)457     private int getBatterySeq(PrintWriter pw) {
458         int seq = mInternal.getBatterySeq();
459         pw.println(seq);
460         return 0;
461     }
462 
getBatteryCharging(PrintWriter pw)463     private int getBatteryCharging(PrintWriter pw) {
464         boolean val = mInternal.isBatteryCharging();
465         pw.println(val);
466         return 0;
467     }
468 
getBatteryNotLow(PrintWriter pw)469     private int getBatteryNotLow(PrintWriter pw) {
470         boolean val = mInternal.isBatteryNotLow();
471         pw.println(val);
472         return 0;
473     }
474 
getConfigValue(PrintWriter pw)475     private int getConfigValue(PrintWriter pw) throws Exception {
476         checkPermission("get device config value", Manifest.permission.DUMP);
477 
478         final String key = getNextArgRequired();
479 
480         final long ident = Binder.clearCallingIdentity();
481         try {
482             pw.println(mInternal.getConfigValue(key));
483             return 0;
484         } finally {
485             Binder.restoreCallingIdentity(ident);
486         }
487     }
488 
getEstimatedNetworkBytes(PrintWriter pw, int byteOption)489     private int getEstimatedNetworkBytes(PrintWriter pw, int byteOption) throws Exception {
490         checkPermission("get estimated bytes");
491 
492         int userId = UserHandle.USER_SYSTEM;
493         String namespace = null;
494 
495         String opt;
496         while ((opt = getNextOption()) != null) {
497             switch (opt) {
498                 case "-u":
499                 case "--user":
500                     userId = UserHandle.parseUserArg(getNextArgRequired());
501                     break;
502 
503                 case "-n":
504                 case "--namespace":
505                     namespace = getNextArgRequired();
506                     break;
507 
508                 default:
509                     pw.println("Error: unknown option '" + opt + "'");
510                     return -1;
511             }
512         }
513 
514         if (userId == UserHandle.USER_CURRENT) {
515             userId = ActivityManager.getCurrentUser();
516         }
517 
518         final String pkgName = getNextArgRequired();
519         final String jobIdStr = getNextArgRequired();
520         final int jobId = Integer.parseInt(jobIdStr);
521 
522         final long ident = Binder.clearCallingIdentity();
523         try {
524             int ret = mInternal.getEstimatedNetworkBytes(pw, pkgName, userId, namespace,
525                     jobId, byteOption);
526             printError(ret, pkgName, userId, namespace, jobId);
527             return ret;
528         } finally {
529             Binder.restoreCallingIdentity(ident);
530         }
531     }
532 
getStorageSeq(PrintWriter pw)533     private int getStorageSeq(PrintWriter pw) {
534         int seq = mInternal.getStorageSeq();
535         pw.println(seq);
536         return 0;
537     }
538 
getStorageNotLow(PrintWriter pw)539     private int getStorageNotLow(PrintWriter pw) {
540         boolean val = mInternal.getStorageNotLow();
541         pw.println(val);
542         return 0;
543     }
544 
getTransferredNetworkBytes(PrintWriter pw, int byteOption)545     private int getTransferredNetworkBytes(PrintWriter pw, int byteOption) throws Exception {
546         checkPermission("get transferred bytes");
547 
548         int userId = UserHandle.USER_SYSTEM;
549         String namespace = null;
550 
551         String opt;
552         while ((opt = getNextOption()) != null) {
553             switch (opt) {
554                 case "-u":
555                 case "--user":
556                     userId = UserHandle.parseUserArg(getNextArgRequired());
557                     break;
558 
559                 case "-n":
560                 case "--namespace":
561                     namespace = getNextArgRequired();
562                     break;
563 
564                 default:
565                     pw.println("Error: unknown option '" + opt + "'");
566                     return -1;
567             }
568         }
569 
570         if (userId == UserHandle.USER_CURRENT) {
571             userId = ActivityManager.getCurrentUser();
572         }
573 
574         final String pkgName = getNextArgRequired();
575         final String jobIdStr = getNextArgRequired();
576         final int jobId = Integer.parseInt(jobIdStr);
577 
578         final long ident = Binder.clearCallingIdentity();
579         try {
580             int ret = mInternal.getTransferredNetworkBytes(pw, pkgName, userId, namespace,
581                     jobId, byteOption);
582             printError(ret, pkgName, userId, namespace, jobId);
583             return ret;
584         } finally {
585             Binder.restoreCallingIdentity(ident);
586         }
587     }
588 
getJobWakelockTag(PrintWriter pw)589     private int getJobWakelockTag(PrintWriter pw) throws Exception {
590         checkPermission("get job wakelock tag");
591 
592         int userId = UserHandle.USER_SYSTEM;
593         String namespace = null;
594 
595         String opt;
596         while ((opt = getNextOption()) != null) {
597             switch (opt) {
598                 case "-u":
599                 case "--user":
600                     userId = UserHandle.parseUserArg(getNextArgRequired());
601                     break;
602 
603                 case "-n":
604                 case "--namespace":
605                     namespace = getNextArgRequired();
606                     break;
607 
608                 default:
609                     pw.println("Error: unknown option '" + opt + "'");
610                     return -1;
611             }
612         }
613 
614         if (userId == UserHandle.USER_CURRENT) {
615             userId = ActivityManager.getCurrentUser();
616         }
617 
618         final String pkgName = getNextArgRequired();
619         final String jobIdStr = getNextArgRequired();
620         final int jobId = Integer.parseInt(jobIdStr);
621 
622         final long ident = Binder.clearCallingIdentity();
623         try {
624             int ret = mInternal.getJobWakelockTag(pw, pkgName, userId, namespace, jobId);
625             printError(ret, pkgName, userId, namespace, jobId);
626             return ret;
627         } finally {
628             Binder.restoreCallingIdentity(ident);
629         }
630     }
631 
getJobState(PrintWriter pw)632     private int getJobState(PrintWriter pw) throws Exception {
633         checkPermission("get job state");
634 
635         int userId = UserHandle.USER_SYSTEM;
636         String namespace = null;
637 
638         String opt;
639         while ((opt = getNextOption()) != null) {
640             switch (opt) {
641                 case "-u":
642                 case "--user":
643                     userId = UserHandle.parseUserArg(getNextArgRequired());
644                     break;
645 
646                 case "-n":
647                 case "--namespace":
648                     namespace = getNextArgRequired();
649                     break;
650 
651                 default:
652                     pw.println("Error: unknown option '" + opt + "'");
653                     return -1;
654             }
655         }
656 
657         if (userId == UserHandle.USER_CURRENT) {
658             userId = ActivityManager.getCurrentUser();
659         }
660 
661         final String pkgName = getNextArgRequired();
662         final String jobIdStr = getNextArgRequired();
663         final int jobId = Integer.parseInt(jobIdStr);
664 
665         final long ident = Binder.clearCallingIdentity();
666         try {
667             int ret = mInternal.getJobState(pw, pkgName, userId, namespace, jobId);
668             printError(ret, pkgName, userId, namespace, jobId);
669             return ret;
670         } finally {
671             Binder.restoreCallingIdentity(ident);
672         }
673     }
674 
doHeartbeat(PrintWriter pw)675     private int doHeartbeat(PrintWriter pw) throws Exception {
676         checkPermission("manipulate scheduler heartbeat");
677 
678         pw.println("Heartbeat command is no longer supported");
679         return -1;
680     }
681 
cacheConfigChanges(PrintWriter pw)682     private int cacheConfigChanges(PrintWriter pw) throws Exception {
683         checkPermission("change config caching", Manifest.permission.DUMP);
684         String opt = getNextArgRequired();
685         boolean enabled;
686         if ("on".equals(opt)) {
687             enabled = true;
688         } else if ("off".equals(opt)) {
689             enabled = false;
690         } else {
691             getErrPrintWriter().println("Error: unknown option " + opt);
692             return 1;
693         }
694         final long ident = Binder.clearCallingIdentity();
695         try {
696             mInternal.setCacheConfigChanges(enabled);
697             pw.println("Config caching " + (enabled ? "enabled" : "disabled"));
698         } finally {
699             Binder.restoreCallingIdentity(ident);
700         }
701         return 0;
702     }
703 
resetFlexPolicy(PrintWriter pw)704     private int resetFlexPolicy(PrintWriter pw) throws Exception {
705         checkPermission("reset flex policy");
706 
707         final long ident = Binder.clearCallingIdentity();
708         try {
709             mInternal.setFlexPolicy(false, 0);
710             pw.println("Reset flex policy to its default state");
711             return 0;
712         } finally {
713             Binder.restoreCallingIdentity(ident);
714         }
715     }
716 
resetExecutionQuota(PrintWriter pw)717     private int resetExecutionQuota(PrintWriter pw) throws Exception {
718         checkPermission("reset execution quota");
719 
720         int userId = UserHandle.USER_SYSTEM;
721 
722         String opt;
723         while ((opt = getNextOption()) != null) {
724             switch (opt) {
725                 case "-u":
726                 case "--user":
727                     userId = UserHandle.parseUserArg(getNextArgRequired());
728                     break;
729 
730                 default:
731                     pw.println("Error: unknown option '" + opt + "'");
732                     return -1;
733             }
734         }
735 
736         if (userId == UserHandle.USER_CURRENT) {
737             userId = ActivityManager.getCurrentUser();
738         }
739 
740         final String pkgName = getNextArgRequired();
741 
742         final long ident = Binder.clearCallingIdentity();
743         try {
744             mInternal.resetExecutionQuota(pkgName, userId);
745         } finally {
746             Binder.restoreCallingIdentity(ident);
747         }
748         return 0;
749     }
750 
resetScheduleQuota(PrintWriter pw)751     private int resetScheduleQuota(PrintWriter pw) throws Exception {
752         checkPermission("reset schedule quota");
753 
754         final long ident = Binder.clearCallingIdentity();
755         try {
756             mInternal.resetScheduleQuota();
757         } finally {
758             Binder.restoreCallingIdentity(ident);
759         }
760         return 0;
761     }
762 
stop(PrintWriter pw)763     private int stop(PrintWriter pw) throws Exception {
764         checkPermission("stop jobs");
765 
766         int userId = UserHandle.USER_ALL;
767         String namespace = null;
768         int stopReason = JobParameters.STOP_REASON_USER;
769         int internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN;
770 
771         String opt;
772         while ((opt = getNextOption()) != null) {
773             switch (opt) {
774                 case "-u":
775                 case "--user":
776                     userId = UserHandle.parseUserArg(getNextArgRequired());
777                     break;
778 
779                 case "-n":
780                 case "--namespace":
781                     namespace = getNextArgRequired();
782                     break;
783 
784                 case "-s":
785                 case "--stop-reason":
786                     stopReason = Integer.parseInt(getNextArgRequired());
787                     break;
788 
789                 case "-i":
790                 case "--internal-stop-reason":
791                     internalStopReason = Integer.parseInt(getNextArgRequired());
792                     break;
793 
794                 default:
795                     pw.println("Error: unknown option '" + opt + "'");
796                     return -1;
797             }
798         }
799 
800         if (userId == UserHandle.USER_CURRENT) {
801             userId = ActivityManager.getCurrentUser();
802         }
803 
804         final String pkgName = getNextArg();
805         final String jobIdStr = getNextArg();
806         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
807 
808         final long ident = Binder.clearCallingIdentity();
809         try {
810             return mInternal.executeStopCommand(pw, pkgName, userId, namespace,
811                     jobIdStr != null, jobId, stopReason, internalStopReason);
812         } finally {
813             Binder.restoreCallingIdentity(ident);
814         }
815     }
816 
triggerDockState(PrintWriter pw)817     private int triggerDockState(PrintWriter pw) throws Exception {
818         checkPermission("trigger wireless charging dock state");
819 
820         final String opt = getNextArgRequired();
821         boolean idleState;
822         if ("idle".equals(opt)) {
823             idleState = true;
824         } else if ("active".equals(opt)) {
825             idleState = false;
826         } else {
827             getErrPrintWriter().println("Error: unknown option " + opt);
828             return 1;
829         }
830 
831         final long ident = Binder.clearCallingIdentity();
832         try {
833             mInternal.triggerDockState(idleState);
834         } finally {
835             Binder.restoreCallingIdentity(ident);
836         }
837         return 0;
838     }
839 
840     @Override
onHelp()841     public void onHelp() {
842         final PrintWriter pw = getOutPrintWriter();
843 
844         pw.println("Job scheduler (jobscheduler) commands:");
845         pw.println("  help");
846         pw.println("    Print this help text.");
847         pw.println("  run [-f | --force] [-s | --satisfied] [-u | --user USER_ID]"
848                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
849         pw.println("    Trigger immediate execution of a specific scheduled job. For historical");
850         pw.println("    reasons, some constraints, such as battery, are ignored when this");
851         pw.println("    command is called. If you don't want any constraints to be ignored,");
852         pw.println("    include the -s flag.");
853         pw.println("    Options:");
854         pw.println("      -f or --force: run the job even if technical constraints such as");
855         pw.println("         connectivity are not currently met. This is incompatible with -f ");
856         pw.println("         and so an error will be reported if both are given.");
857         pw.println("      -n or --namespace: specify the namespace this job sits in; the default");
858         pw.println("         is null (no namespace).");
859         pw.println("      -s or --satisfied: run the job only if all constraints are met.");
860         pw.println("         This is incompatible with -f and so an error will be reported");
861         pw.println("         if both are given.");
862         pw.println("      -u or --user: specify which user's job is to be run; the default is");
863         pw.println("         the primary or system user");
864         pw.println("  stop [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
865                 + " [-s | --stop-reason STOP_REASON] [-i | --internal-stop-reason STOP_REASON]"
866                 + " [PACKAGE] [JOB_ID]");
867         pw.println("    Trigger immediate stop of currently executing jobs using the specified");
868         pw.println("    stop reasons.");
869         pw.println("    Options:");
870         pw.println("      -u or --user: specify which user's job is to be run; the default is");
871         pw.println("         all users");
872         pw.println("      -n or --namespace: specify the namespace this job sits in; the default");
873         pw.println("         is null (no namespace).");
874         pw.println("      -s or --stop-reason: specify the stop reason given to the job.");
875         pw.println("         Valid values are those that can be returned from");
876         pw.println("         JobParameters.getStopReason().");
877         pw.println("          The default value is STOP_REASON_USER.");
878         pw.println("      -i or --internal-stop-reason: specify the internal stop reason.");
879         pw.println("         JobScheduler will use for internal processing.");
880         pw.println("         Valid values are those that can be returned from");
881         pw.println("         JobParameters.getInternalStopReason().");
882         pw.println("          The default value is INTERNAL_STOP_REASON_UNDEFINED.");
883         pw.println("  timeout [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
884                 + " [PACKAGE] [JOB_ID]");
885         pw.println("    Trigger immediate timeout of currently executing jobs, as if their");
886         pw.println("    execution timeout had expired.");
887         pw.println("    This is the equivalent of calling `stop -s 3 -i 3`.");
888         pw.println("    Options:");
889         pw.println("      -u or --user: specify which user's job is to be run; the default is");
890         pw.println("         all users");
891         pw.println("      -n or --namespace: specify the namespace this job sits in; the default");
892         pw.println("         is null (no namespace).");
893         pw.println("  cancel [-u | --user USER_ID] [-n | --namespace NAMESPACE] PACKAGE [JOB_ID]");
894         pw.println("    Cancel a scheduled job.  If a job ID is not supplied, all jobs scheduled");
895         pw.println("    by that package will be canceled.  USE WITH CAUTION.");
896         pw.println("    Options:");
897         pw.println("      -u or --user: specify which user's job is to be run; the default is");
898         pw.println("         the primary or system user");
899         pw.println("      -n or --namespace: specify the namespace this job sits in; the default");
900         pw.println("         is null (no namespace).");
901         pw.println("  heartbeat [num]");
902         pw.println("    No longer used.");
903         pw.println("  cache-config-changes [on|off]");
904         pw.println("    Control caching the set of most recently processed config flags.");
905         pw.println("    Off by default.  Turning on makes get-config-value useful.");
906         pw.println("  monitor-battery [on|off]");
907         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
908         pw.println("    on makes get-battery-seq useful.");
909         pw.println("  enable-flex-policy --option <option>");
910         pw.println("    Enable flex policy with the specified options. Supported options are");
911         pw.println("    battery-not-low, charging, connectivity, idle.");
912         pw.println("    Multiple enable options can be specified (e.g.");
913         pw.println("    enable-flex-policy --option battery-not-low --option charging");
914         pw.println("  disable-flex-policy");
915         pw.println("    Turn off flex policy so that it does not affect job execution.");
916         pw.println("  reset-flex-policy");
917         pw.println("    Resets the flex policy to its default state.");
918         pw.println("  get-aconfig-flag-state FULL_FLAG_NAME");
919         pw.println("    Return the state of the specified aconfig flag, if known. The flag name");
920         pw.println("         must be fully qualified.");
921         pw.println("  get-battery-seq");
922         pw.println("    Return the last battery update sequence number that was received.");
923         pw.println("  get-battery-charging");
924         pw.println("    Return whether the battery is currently considered to be charging.");
925         pw.println("  get-battery-not-low");
926         pw.println("    Return whether the battery is currently considered to not be low.");
927         pw.println("  get-config-value KEY");
928         pw.println("    Return the most recently processed and cached config value for the KEY.");
929         pw.println("    Only useful if caching is turned on with cache-config-changes.");
930         pw.println("  get-estimated-download-bytes [-u | --user USER_ID]"
931                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
932         pw.println("    Return the most recent estimated download bytes for the job.");
933         pw.println("    Options:");
934         pw.println("      -u or --user: specify which user's job is to be run; the default is");
935         pw.println("         the primary or system user");
936         pw.println("  get-estimated-upload-bytes [-u | --user USER_ID]"
937                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
938         pw.println("    Return the most recent estimated upload bytes for the job.");
939         pw.println("    Options:");
940         pw.println("      -u or --user: specify which user's job is to be run; the default is");
941         pw.println("         the primary or system user");
942         pw.println("  get-storage-seq");
943         pw.println("    Return the last storage update sequence number that was received.");
944         pw.println("  get-storage-not-low");
945         pw.println("    Return whether storage is currently considered to not be low.");
946         pw.println("  get-transferred-download-bytes [-u | --user USER_ID]"
947                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
948         pw.println("    Return the most recent transferred download bytes for the job.");
949         pw.println("    Options:");
950         pw.println("      -u or --user: specify which user's job is to be run; the default is");
951         pw.println("         the primary or system user");
952         pw.println("  get-transferred-upload-bytes [-u | --user USER_ID]"
953                 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
954         pw.println("    Return the most recent transferred upload bytes for the job.");
955         pw.println("    Options:");
956         pw.println("      -u or --user: specify which user's job is to be run; the default is");
957         pw.println("         the primary or system user");
958         pw.println("  get-job-state [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
959                 + " PACKAGE JOB_ID");
960         pw.println("    Return the current state of a job, may be any combination of:");
961         pw.println("      pending: currently on the pending list, waiting to be active");
962         pw.println("      active: job is actively running");
963         pw.println("      user-stopped: job can't run because its user is stopped");
964         pw.println("      backing-up: job can't run because app is currently backing up its data");
965         pw.println("      no-component: job can't run because its component is not available");
966         pw.println("      ready: job is ready to run (all constraints satisfied or bypassed)");
967         pw.println("      waiting: if nothing else above is printed, job not ready to run");
968         pw.println("    Options:");
969         pw.println("      -u or --user: specify which user's job is to be run; the default is");
970         pw.println("         the primary or system user");
971         pw.println("      -n or --namespace: specify the namespace this job sits in; the default");
972         pw.println("         is null (no namespace).");
973         pw.println("  trigger-dock-state [idle|active]");
974         pw.println("    Trigger wireless charging dock state.  Active by default.");
975         pw.println();
976     }
977 }
978