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.annotation.Nullable; 20 import android.app.ActivityManager; 21 import android.app.AppGlobals; 22 import android.app.job.JobParameters; 23 import android.content.pm.IPackageManager; 24 import android.content.pm.PackageManager; 25 import android.os.Binder; 26 import android.os.UserHandle; 27 28 import com.android.modules.utils.BasicShellCommandHandler; 29 30 import java.io.PrintWriter; 31 32 public 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 JobSchedulerShellCommand(JobSchedulerService service)43 JobSchedulerShellCommand(JobSchedulerService service) { 44 mInternal = service; 45 mPM = AppGlobals.getPackageManager(); 46 } 47 48 @Override onCommand(String cmd)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 checkPermission(String operation)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 printError(int errCode, String pkgName, int userId, @Nullable String namespace, int jobId)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 runJob(PrintWriter pw)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 timeout(PrintWriter pw)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 cancelJob(PrintWriter pw)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 monitorBattery(PrintWriter pw)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 getBatterySeq(PrintWriter pw)335 private int getBatterySeq(PrintWriter pw) { 336 int seq = mInternal.getBatterySeq(); 337 pw.println(seq); 338 return 0; 339 } 340 getBatteryCharging(PrintWriter pw)341 private int getBatteryCharging(PrintWriter pw) { 342 boolean val = mInternal.isBatteryCharging(); 343 pw.println(val); 344 return 0; 345 } 346 getBatteryNotLow(PrintWriter pw)347 private int getBatteryNotLow(PrintWriter pw) { 348 boolean val = mInternal.isBatteryNotLow(); 349 pw.println(val); 350 return 0; 351 } 352 getEstimatedNetworkBytes(PrintWriter pw, int byteOption)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 getStorageSeq(PrintWriter pw)397 private int getStorageSeq(PrintWriter pw) { 398 int seq = mInternal.getStorageSeq(); 399 pw.println(seq); 400 return 0; 401 } 402 getStorageNotLow(PrintWriter pw)403 private int getStorageNotLow(PrintWriter pw) { 404 boolean val = mInternal.getStorageNotLow(); 405 pw.println(val); 406 return 0; 407 } 408 getTransferredNetworkBytes(PrintWriter pw, int byteOption)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 getJobState(PrintWriter pw)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 doHeartbeat(PrintWriter pw)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 resetExecutionQuota(PrintWriter pw)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 resetScheduleQuota(PrintWriter pw)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 stop(PrintWriter pw)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 triggerDockState(PrintWriter pw)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 onHelp()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 } 746