blob: 4357d4f39dce3d266fecbb5adaf34798c0ec6b12 [file] [log] [blame]
Aurimas Liutikas88c7ff12023-08-10 12:42:26 -07001/*
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
17package com.android.server.job;
18
19import android.annotation.Nullable;
20import android.app.ActivityManager;
21import android.app.AppGlobals;
22import android.app.job.JobParameters;
23import android.content.pm.IPackageManager;
24import android.content.pm.PackageManager;
25import android.os.Binder;
26import android.os.UserHandle;
27
28import com.android.modules.utils.BasicShellCommandHandler;
29
30import java.io.PrintWriter;
31
32public final class JobSchedulerShellCommand extends BasicShellCommandHandler {
33 public static final int CMD_ERR_NO_PACKAGE = -1000;
34 public static final int CMD_ERR_NO_JOB = -1001;
35 public static final int CMD_ERR_CONSTRAINTS = -1002;
36
37 static final int BYTE_OPTION_DOWNLOAD = 0;
38 static final int BYTE_OPTION_UPLOAD = 1;
39
40 JobSchedulerService mInternal;
41 IPackageManager mPM;
42
43 JobSchedulerShellCommand(JobSchedulerService service) {
44 mInternal = service;
45 mPM = AppGlobals.getPackageManager();
46 }
47
48 @Override
49 public int onCommand(String cmd) {
50 final PrintWriter pw = getOutPrintWriter();
51 try {
52 switch (cmd != null ? cmd : "") {
53 case "run":
54 return runJob(pw);
55 case "timeout":
56 return timeout(pw);
57 case "cancel":
58 return cancelJob(pw);
59 case "monitor-battery":
60 return monitorBattery(pw);
61 case "get-battery-seq":
62 return getBatterySeq(pw);
63 case "get-battery-charging":
64 return getBatteryCharging(pw);
65 case "get-battery-not-low":
66 return getBatteryNotLow(pw);
67 case "get-estimated-download-bytes":
68 return getEstimatedNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
69 case "get-estimated-upload-bytes":
70 return getEstimatedNetworkBytes(pw, BYTE_OPTION_UPLOAD);
71 case "get-storage-seq":
72 return getStorageSeq(pw);
73 case "get-storage-not-low":
74 return getStorageNotLow(pw);
75 case "get-transferred-download-bytes":
76 return getTransferredNetworkBytes(pw, BYTE_OPTION_DOWNLOAD);
77 case "get-transferred-upload-bytes":
78 return getTransferredNetworkBytes(pw, BYTE_OPTION_UPLOAD);
79 case "get-job-state":
80 return getJobState(pw);
81 case "heartbeat":
82 return doHeartbeat(pw);
83 case "reset-execution-quota":
84 return resetExecutionQuota(pw);
85 case "reset-schedule-quota":
86 return resetScheduleQuota(pw);
87 case "stop":
88 return stop(pw);
89 case "trigger-dock-state":
90 return triggerDockState(pw);
91 default:
92 return handleDefaultCommands(cmd);
93 }
94 } catch (Exception e) {
95 pw.println("Exception: " + e);
96 }
97 return -1;
98 }
99
100 private void checkPermission(String operation) throws Exception {
101 final int uid = Binder.getCallingUid();
102 if (uid == 0) {
103 // Root can do anything.
104 return;
105 }
106 final int perm = mPM.checkUidPermission(
107 "android.permission.CHANGE_APP_IDLE_STATE", uid);
108 if (perm != PackageManager.PERMISSION_GRANTED) {
109 throw new SecurityException("Uid " + uid
110 + " not permitted to " + operation);
111 }
112 }
113
114 private boolean printError(int errCode, String pkgName, int userId, @Nullable String namespace,
115 int jobId) {
116 PrintWriter pw;
117 switch (errCode) {
118 case CMD_ERR_NO_PACKAGE:
119 pw = getErrPrintWriter();
120 pw.print("Package not found: ");
121 pw.print(pkgName);
122 pw.print(" / user ");
123 pw.println(userId);
124 return true;
125
126 case CMD_ERR_NO_JOB:
127 pw = getErrPrintWriter();
128 pw.print("Could not find job ");
129 pw.print(jobId);
130 pw.print(" in package ");
131 pw.print(pkgName);
132 if (namespace != null) {
133 pw.print(" / namespace ");
134 pw.print(namespace);
135 }
136 pw.print(" / user ");
137 pw.println(userId);
138 return true;
139
140 case CMD_ERR_CONSTRAINTS:
141 pw = getErrPrintWriter();
142 pw.print("Job ");
143 pw.print(jobId);
144 pw.print(" in package ");
145 pw.print(pkgName);
146 if (namespace != null) {
147 pw.print(" / namespace ");
148 pw.print(namespace);
149 }
150 pw.print(" / user ");
151 pw.print(userId);
152 pw.println(" has functional constraints but --force not specified");
153 return true;
154
155 default:
156 return false;
157 }
158 }
159
160 private int runJob(PrintWriter pw) throws Exception {
161 checkPermission("force scheduled jobs");
162
163 boolean force = false;
164 boolean satisfied = false;
165 int userId = UserHandle.USER_SYSTEM;
166 String namespace = null;
167
168 String opt;
169 while ((opt = getNextOption()) != null) {
170 switch (opt) {
171 case "-f":
172 case "--force":
173 force = true;
174 break;
175
176 case "-s":
177 case "--satisfied":
178 satisfied = true;
179 break;
180
181 case "-u":
182 case "--user":
183 userId = Integer.parseInt(getNextArgRequired());
184 break;
185
186 case "-n":
187 case "--namespace":
188 namespace = getNextArgRequired();
189 break;
190
191 default:
192 pw.println("Error: unknown option '" + opt + "'");
193 return -1;
194 }
195 }
196
197 if (force && satisfied) {
198 pw.println("Cannot specify both --force and --satisfied");
199 return -1;
200 }
201
202 final String pkgName = getNextArgRequired();
203 final int jobId = Integer.parseInt(getNextArgRequired());
204
205 final long ident = Binder.clearCallingIdentity();
206 try {
207 int ret = mInternal.executeRunCommand(pkgName, userId, namespace,
208 jobId, satisfied, force);
209 if (printError(ret, pkgName, userId, namespace, jobId)) {
210 return ret;
211 }
212
213 // success!
214 pw.print("Running job");
215 if (force) {
216 pw.print(" [FORCED]");
217 }
218 pw.println();
219
220 return ret;
221 } finally {
222 Binder.restoreCallingIdentity(ident);
223 }
224 }
225
226 private int timeout(PrintWriter pw) throws Exception {
227 checkPermission("force timeout jobs");
228
229 int userId = UserHandle.USER_ALL;
230 String namespace = null;
231
232 String opt;
233 while ((opt = getNextOption()) != null) {
234 switch (opt) {
235 case "-u":
236 case "--user":
237 userId = UserHandle.parseUserArg(getNextArgRequired());
238 break;
239
240 case "-n":
241 case "--namespace":
242 namespace = getNextArgRequired();
243 break;
244
245 default:
246 pw.println("Error: unknown option '" + opt + "'");
247 return -1;
248 }
249 }
250
251 if (userId == UserHandle.USER_CURRENT) {
252 userId = ActivityManager.getCurrentUser();
253 }
254
255 final String pkgName = getNextArg();
256 final String jobIdStr = getNextArg();
257 final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
258
259 final long ident = Binder.clearCallingIdentity();
260 try {
261 return mInternal.executeStopCommand(pw, pkgName, userId, namespace,
262 jobIdStr != null, jobId,
263 JobParameters.STOP_REASON_TIMEOUT, JobParameters.INTERNAL_STOP_REASON_TIMEOUT);
264 } finally {
265 Binder.restoreCallingIdentity(ident);
266 }
267 }
268
269 private int cancelJob(PrintWriter pw) throws Exception {
270 checkPermission("cancel jobs");
271
272 int userId = UserHandle.USER_SYSTEM;
273 String namespace = null;
274
275 String opt;
276 while ((opt = getNextOption()) != null) {
277 switch (opt) {
278 case "-u":
279 case "--user":
280 userId = UserHandle.parseUserArg(getNextArgRequired());
281 break;
282
283 case "-n":
284 case "--namespace":
285 namespace = getNextArgRequired();
286 break;
287
288 default:
289 pw.println("Error: unknown option '" + opt + "'");
290 return -1;
291 }
292 }
293
294 if (userId < 0) {
295 pw.println("Error: must specify a concrete user ID");
296 return -1;
297 }
298
299 final String pkgName = getNextArg();
300 final String jobIdStr = getNextArg();
301 final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
302
303 final long ident = Binder.clearCallingIdentity();
304 try {
305 return mInternal.executeCancelCommand(pw, pkgName, userId, namespace,
306 jobIdStr != null, jobId);
307 } finally {
308 Binder.restoreCallingIdentity(ident);
309 }
310 }
311
312 private int monitorBattery(PrintWriter pw) throws Exception {
313 checkPermission("change battery monitoring");
314 String opt = getNextArgRequired();
315 boolean enabled;
316 if ("on".equals(opt)) {
317 enabled = true;
318 } else if ("off".equals(opt)) {
319 enabled = false;
320 } else {
321 getErrPrintWriter().println("Error: unknown option " + opt);
322 return 1;
323 }
324 final long ident = Binder.clearCallingIdentity();
325 try {
326 mInternal.setMonitorBattery(enabled);
327 if (enabled) pw.println("Battery monitoring enabled");
328 else pw.println("Battery monitoring disabled");
329 } finally {
330 Binder.restoreCallingIdentity(ident);
331 }
332 return 0;
333 }
334
335 private int getBatterySeq(PrintWriter pw) {
336 int seq = mInternal.getBatterySeq();
337 pw.println(seq);
338 return 0;
339 }
340
341 private int getBatteryCharging(PrintWriter pw) {
342 boolean val = mInternal.isBatteryCharging();
343 pw.println(val);
344 return 0;
345 }
346
347 private int getBatteryNotLow(PrintWriter pw) {
348 boolean val = mInternal.isBatteryNotLow();
349 pw.println(val);
350 return 0;
351 }
352
353 private int getEstimatedNetworkBytes(PrintWriter pw, int byteOption) throws Exception {
354 checkPermission("get estimated bytes");
355
356 int userId = UserHandle.USER_SYSTEM;
357 String namespace = null;
358
359 String opt;
360 while ((opt = getNextOption()) != null) {
361 switch (opt) {
362 case "-u":
363 case "--user":
364 userId = UserHandle.parseUserArg(getNextArgRequired());
365 break;
366
367 case "-n":
368 case "--namespace":
369 namespace = getNextArgRequired();
370 break;
371
372 default:
373 pw.println("Error: unknown option '" + opt + "'");
374 return -1;
375 }
376 }
377
378 if (userId == UserHandle.USER_CURRENT) {
379 userId = ActivityManager.getCurrentUser();
380 }
381
382 final String pkgName = getNextArgRequired();
383 final String jobIdStr = getNextArgRequired();
384 final int jobId = Integer.parseInt(jobIdStr);
385
386 final long ident = Binder.clearCallingIdentity();
387 try {
388 int ret = mInternal.getEstimatedNetworkBytes(pw, pkgName, userId, namespace,
389 jobId, byteOption);
390 printError(ret, pkgName, userId, namespace, jobId);
391 return ret;
392 } finally {
393 Binder.restoreCallingIdentity(ident);
394 }
395 }
396
397 private int getStorageSeq(PrintWriter pw) {
398 int seq = mInternal.getStorageSeq();
399 pw.println(seq);
400 return 0;
401 }
402
403 private int getStorageNotLow(PrintWriter pw) {
404 boolean val = mInternal.getStorageNotLow();
405 pw.println(val);
406 return 0;
407 }
408
409 private int getTransferredNetworkBytes(PrintWriter pw, int byteOption) throws Exception {
410 checkPermission("get transferred bytes");
411
412 int userId = UserHandle.USER_SYSTEM;
413 String namespace = null;
414
415 String opt;
416 while ((opt = getNextOption()) != null) {
417 switch (opt) {
418 case "-u":
419 case "--user":
420 userId = UserHandle.parseUserArg(getNextArgRequired());
421 break;
422
423 case "-n":
424 case "--namespace":
425 namespace = getNextArgRequired();
426 break;
427
428 default:
429 pw.println("Error: unknown option '" + opt + "'");
430 return -1;
431 }
432 }
433
434 if (userId == UserHandle.USER_CURRENT) {
435 userId = ActivityManager.getCurrentUser();
436 }
437
438 final String pkgName = getNextArgRequired();
439 final String jobIdStr = getNextArgRequired();
440 final int jobId = Integer.parseInt(jobIdStr);
441
442 final long ident = Binder.clearCallingIdentity();
443 try {
444 int ret = mInternal.getTransferredNetworkBytes(pw, pkgName, userId, namespace,
445 jobId, byteOption);
446 printError(ret, pkgName, userId, namespace, jobId);
447 return ret;
448 } finally {
449 Binder.restoreCallingIdentity(ident);
450 }
451 }
452
453 private int getJobState(PrintWriter pw) throws Exception {
454 checkPermission("get job state");
455
456 int userId = UserHandle.USER_SYSTEM;
457 String namespace = null;
458
459 String opt;
460 while ((opt = getNextOption()) != null) {
461 switch (opt) {
462 case "-u":
463 case "--user":
464 userId = UserHandle.parseUserArg(getNextArgRequired());
465 break;
466
467 case "-n":
468 case "--namespace":
469 namespace = getNextArgRequired();
470 break;
471
472 default:
473 pw.println("Error: unknown option '" + opt + "'");
474 return -1;
475 }
476 }
477
478 if (userId == UserHandle.USER_CURRENT) {
479 userId = ActivityManager.getCurrentUser();
480 }
481
482 final String pkgName = getNextArgRequired();
483 final String jobIdStr = getNextArgRequired();
484 final int jobId = Integer.parseInt(jobIdStr);
485
486 final long ident = Binder.clearCallingIdentity();
487 try {
488 int ret = mInternal.getJobState(pw, pkgName, userId, namespace, jobId);
489 printError(ret, pkgName, userId, namespace, jobId);
490 return ret;
491 } finally {
492 Binder.restoreCallingIdentity(ident);
493 }
494 }
495
496 private int doHeartbeat(PrintWriter pw) throws Exception {
497 checkPermission("manipulate scheduler heartbeat");
498
499 pw.println("Heartbeat command is no longer supported");
500 return -1;
501 }
502
503 private int resetExecutionQuota(PrintWriter pw) throws Exception {
504 checkPermission("reset execution quota");
505
506 int userId = UserHandle.USER_SYSTEM;
507
508 String opt;
509 while ((opt = getNextOption()) != null) {
510 switch (opt) {
511 case "-u":
512 case "--user":
513 userId = UserHandle.parseUserArg(getNextArgRequired());
514 break;
515
516 default:
517 pw.println("Error: unknown option '" + opt + "'");
518 return -1;
519 }
520 }
521
522 if (userId == UserHandle.USER_CURRENT) {
523 userId = ActivityManager.getCurrentUser();
524 }
525
526 final String pkgName = getNextArgRequired();
527
528 final long ident = Binder.clearCallingIdentity();
529 try {
530 mInternal.resetExecutionQuota(pkgName, userId);
531 } finally {
532 Binder.restoreCallingIdentity(ident);
533 }
534 return 0;
535 }
536
537 private int resetScheduleQuota(PrintWriter pw) throws Exception {
538 checkPermission("reset schedule quota");
539
540 final long ident = Binder.clearCallingIdentity();
541 try {
542 mInternal.resetScheduleQuota();
543 } finally {
544 Binder.restoreCallingIdentity(ident);
545 }
546 return 0;
547 }
548
549 private int stop(PrintWriter pw) throws Exception {
550 checkPermission("stop jobs");
551
552 int userId = UserHandle.USER_ALL;
553 String namespace = null;
554 int stopReason = JobParameters.STOP_REASON_USER;
555 int internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN;
556
557 String opt;
558 while ((opt = getNextOption()) != null) {
559 switch (opt) {
560 case "-u":
561 case "--user":
562 userId = UserHandle.parseUserArg(getNextArgRequired());
563 break;
564
565 case "-n":
566 case "--namespace":
567 namespace = getNextArgRequired();
568 break;
569
570 case "-s":
571 case "--stop-reason":
572 stopReason = Integer.parseInt(getNextArgRequired());
573 break;
574
575 case "-i":
576 case "--internal-stop-reason":
577 internalStopReason = Integer.parseInt(getNextArgRequired());
578 break;
579
580 default:
581 pw.println("Error: unknown option '" + opt + "'");
582 return -1;
583 }
584 }
585
586 if (userId == UserHandle.USER_CURRENT) {
587 userId = ActivityManager.getCurrentUser();
588 }
589
590 final String pkgName = getNextArg();
591 final String jobIdStr = getNextArg();
592 final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
593
594 final long ident = Binder.clearCallingIdentity();
595 try {
596 return mInternal.executeStopCommand(pw, pkgName, userId, namespace,
597 jobIdStr != null, jobId, stopReason, internalStopReason);
598 } finally {
599 Binder.restoreCallingIdentity(ident);
600 }
601 }
602
603 private int triggerDockState(PrintWriter pw) throws Exception {
604 checkPermission("trigger wireless charging dock state");
605
606 final String opt = getNextArgRequired();
607 boolean idleState;
608 if ("idle".equals(opt)) {
609 idleState = true;
610 } else if ("active".equals(opt)) {
611 idleState = false;
612 } else {
613 getErrPrintWriter().println("Error: unknown option " + opt);
614 return 1;
615 }
616
617 final long ident = Binder.clearCallingIdentity();
618 try {
619 mInternal.triggerDockState(idleState);
620 } finally {
621 Binder.restoreCallingIdentity(ident);
622 }
623 return 0;
624 }
625
626 @Override
627 public void onHelp() {
628 final PrintWriter pw = getOutPrintWriter();
629
630 pw.println("Job scheduler (jobscheduler) commands:");
631 pw.println(" help");
632 pw.println(" Print this help text.");
633 pw.println(" run [-f | --force] [-s | --satisfied] [-u | --user USER_ID]"
634 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
635 pw.println(" Trigger immediate execution of a specific scheduled job. For historical");
636 pw.println(" reasons, some constraints, such as battery, are ignored when this");
637 pw.println(" command is called. If you don't want any constraints to be ignored,");
638 pw.println(" include the -s flag.");
639 pw.println(" Options:");
640 pw.println(" -f or --force: run the job even if technical constraints such as");
641 pw.println(" connectivity are not currently met. This is incompatible with -f ");
642 pw.println(" and so an error will be reported if both are given.");
643 pw.println(" -n or --namespace: specify the namespace this job sits in; the default");
644 pw.println(" is null (no namespace).");
645 pw.println(" -s or --satisfied: run the job only if all constraints are met.");
646 pw.println(" This is incompatible with -f and so an error will be reported");
647 pw.println(" if both are given.");
648 pw.println(" -u or --user: specify which user's job is to be run; the default is");
649 pw.println(" the primary or system user");
650 pw.println(" stop [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
651 + " [-s | --stop-reason STOP_REASON] [-i | --internal-stop-reason STOP_REASON]"
652 + " [PACKAGE] [JOB_ID]");
653 pw.println(" Trigger immediate stop of currently executing jobs using the specified");
654 pw.println(" stop reasons.");
655 pw.println(" Options:");
656 pw.println(" -u or --user: specify which user's job is to be run; the default is");
657 pw.println(" all users");
658 pw.println(" -n or --namespace: specify the namespace this job sits in; the default");
659 pw.println(" is null (no namespace).");
660 pw.println(" -s or --stop-reason: specify the stop reason given to the job.");
661 pw.println(" Valid values are those that can be returned from");
662 pw.println(" JobParameters.getStopReason().");
663 pw.println(" The default value is STOP_REASON_USER.");
664 pw.println(" -i or --internal-stop-reason: specify the internal stop reason.");
665 pw.println(" JobScheduler will use for internal processing.");
666 pw.println(" Valid values are those that can be returned from");
667 pw.println(" JobParameters.getInternalStopReason().");
668 pw.println(" The default value is INTERNAL_STOP_REASON_UNDEFINED.");
669 pw.println(" timeout [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
670 + " [PACKAGE] [JOB_ID]");
671 pw.println(" Trigger immediate timeout of currently executing jobs, as if their");
672 pw.println(" execution timeout had expired.");
673 pw.println(" This is the equivalent of calling `stop -s 3 -i 3`.");
674 pw.println(" Options:");
675 pw.println(" -u or --user: specify which user's job is to be run; the default is");
676 pw.println(" all users");
677 pw.println(" -n or --namespace: specify the namespace this job sits in; the default");
678 pw.println(" is null (no namespace).");
679 pw.println(" cancel [-u | --user USER_ID] [-n | --namespace NAMESPACE] PACKAGE [JOB_ID]");
680 pw.println(" Cancel a scheduled job. If a job ID is not supplied, all jobs scheduled");
681 pw.println(" by that package will be canceled. USE WITH CAUTION.");
682 pw.println(" Options:");
683 pw.println(" -u or --user: specify which user's job is to be run; the default is");
684 pw.println(" the primary or system user");
685 pw.println(" -n or --namespace: specify the namespace this job sits in; the default");
686 pw.println(" is null (no namespace).");
687 pw.println(" heartbeat [num]");
688 pw.println(" No longer used.");
689 pw.println(" monitor-battery [on|off]");
690 pw.println(" Control monitoring of all battery changes. Off by default. Turning");
691 pw.println(" on makes get-battery-seq useful.");
692 pw.println(" get-battery-seq");
693 pw.println(" Return the last battery update sequence number that was received.");
694 pw.println(" get-battery-charging");
695 pw.println(" Return whether the battery is currently considered to be charging.");
696 pw.println(" get-battery-not-low");
697 pw.println(" Return whether the battery is currently considered to not be low.");
698 pw.println(" get-estimated-download-bytes [-u | --user USER_ID]"
699 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
700 pw.println(" Return the most recent estimated download bytes for the job.");
701 pw.println(" Options:");
702 pw.println(" -u or --user: specify which user's job is to be run; the default is");
703 pw.println(" the primary or system user");
704 pw.println(" get-estimated-upload-bytes [-u | --user USER_ID]"
705 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
706 pw.println(" Return the most recent estimated upload bytes for the job.");
707 pw.println(" Options:");
708 pw.println(" -u or --user: specify which user's job is to be run; the default is");
709 pw.println(" the primary or system user");
710 pw.println(" get-storage-seq");
711 pw.println(" Return the last storage update sequence number that was received.");
712 pw.println(" get-storage-not-low");
713 pw.println(" Return whether storage is currently considered to not be low.");
714 pw.println(" get-transferred-download-bytes [-u | --user USER_ID]"
715 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
716 pw.println(" Return the most recent transferred download bytes for the job.");
717 pw.println(" Options:");
718 pw.println(" -u or --user: specify which user's job is to be run; the default is");
719 pw.println(" the primary or system user");
720 pw.println(" get-transferred-upload-bytes [-u | --user USER_ID]"
721 + " [-n | --namespace NAMESPACE] PACKAGE JOB_ID");
722 pw.println(" Return the most recent transferred upload bytes for the job.");
723 pw.println(" Options:");
724 pw.println(" -u or --user: specify which user's job is to be run; the default is");
725 pw.println(" the primary or system user");
726 pw.println(" get-job-state [-u | --user USER_ID] [-n | --namespace NAMESPACE]"
727 + " PACKAGE JOB_ID");
728 pw.println(" Return the current state of a job, may be any combination of:");
729 pw.println(" pending: currently on the pending list, waiting to be active");
730 pw.println(" active: job is actively running");
731 pw.println(" user-stopped: job can't run because its user is stopped");
732 pw.println(" backing-up: job can't run because app is currently backing up its data");
733 pw.println(" no-component: job can't run because its component is not available");
734 pw.println(" ready: job is ready to run (all constraints satisfied or bypassed)");
735 pw.println(" waiting: if nothing else above is printed, job not ready to run");
736 pw.println(" Options:");
737 pw.println(" -u or --user: specify which user's job is to be run; the default is");
738 pw.println(" the primary or system user");
739 pw.println(" -n or --namespace: specify the namespace this job sits in; the default");
740 pw.println(" is null (no namespace).");
741 pw.println(" trigger-dock-state [idle|active]");
742 pw.println(" Trigger wireless charging dock state. Active by default.");
743 pw.println();
744 }
745}