Home | History | Annotate | Download | only in job
      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.Binder;
     24 import android.os.ShellCommand;
     25 import android.os.UserHandle;
     26 
     27 import java.io.PrintWriter;
     28 
     29 public final class JobSchedulerShellCommand extends ShellCommand {
     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 
     37     JobSchedulerShellCommand(JobSchedulerService service) {
     38         mInternal = service;
     39         mPM = AppGlobals.getPackageManager();
     40     }
     41 
     42     @Override
     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 "monitor-battery":
     52                     return monitorBattery(pw);
     53                 case "get-battery-seq":
     54                     return getBatterySeq(pw);
     55                 case "get-battery-charging":
     56                     return getBatteryCharging(pw);
     57                 case "get-battery-not-low":
     58                     return getBatteryNotLow(pw);
     59                 case "get-storage-seq":
     60                     return getStorageSeq(pw);
     61                 case "get-storage-not-low":
     62                     return getStorageNotLow(pw);
     63                 case "get-job-state":
     64                     return getJobState(pw);
     65                 default:
     66                     return handleDefaultCommands(cmd);
     67             }
     68         } catch (Exception e) {
     69             pw.println("Exception: " + e);
     70         }
     71         return -1;
     72     }
     73 
     74     private void checkPermission(String operation) throws Exception {
     75         final int uid = Binder.getCallingUid();
     76         if (uid == 0) {
     77             // Root can do anything.
     78             return;
     79         }
     80         final int perm = mPM.checkUidPermission(
     81                 "android.permission.CHANGE_APP_IDLE_STATE", uid);
     82         if (perm != PackageManager.PERMISSION_GRANTED) {
     83             throw new SecurityException("Uid " + uid
     84                     + " not permitted to " + operation);
     85         }
     86     }
     87 
     88     private boolean printError(int errCode, String pkgName, int userId, int jobId) {
     89         PrintWriter pw;
     90         switch (errCode) {
     91             case CMD_ERR_NO_PACKAGE:
     92                 pw = getErrPrintWriter();
     93                 pw.print("Package not found: ");
     94                 pw.print(pkgName);
     95                 pw.print(" / user ");
     96                 pw.println(userId);
     97                 return true;
     98 
     99             case CMD_ERR_NO_JOB:
    100                 pw = getErrPrintWriter();
    101                 pw.print("Could not find job ");
    102                 pw.print(jobId);
    103                 pw.print(" in package ");
    104                 pw.print(pkgName);
    105                 pw.print(" / user ");
    106                 pw.println(userId);
    107                 return true;
    108 
    109             case CMD_ERR_CONSTRAINTS:
    110                 pw = getErrPrintWriter();
    111                 pw.print("Job ");
    112                 pw.print(jobId);
    113                 pw.print(" in package ");
    114                 pw.print(pkgName);
    115                 pw.print(" / user ");
    116                 pw.print(userId);
    117                 pw.println(" has functional constraints but --force not specified");
    118                 return true;
    119 
    120             default:
    121                 return false;
    122         }
    123     }
    124 
    125     private int runJob(PrintWriter pw) throws Exception {
    126         checkPermission("force scheduled jobs");
    127 
    128         boolean force = false;
    129         int userId = UserHandle.USER_SYSTEM;
    130 
    131         String opt;
    132         while ((opt = getNextOption()) != null) {
    133             switch (opt) {
    134                 case "-f":
    135                 case "--force":
    136                     force = true;
    137                     break;
    138 
    139                 case "-u":
    140                 case "--user":
    141                     userId = Integer.parseInt(getNextArgRequired());
    142                     break;
    143 
    144                 default:
    145                     pw.println("Error: unknown option '" + opt + "'");
    146                     return -1;
    147             }
    148         }
    149 
    150         final String pkgName = getNextArgRequired();
    151         final int jobId = Integer.parseInt(getNextArgRequired());
    152 
    153         final long ident = Binder.clearCallingIdentity();
    154         try {
    155             int ret = mInternal.executeRunCommand(pkgName, userId, jobId, force);
    156             if (printError(ret, pkgName, userId, jobId)) {
    157                 return ret;
    158             }
    159 
    160             // success!
    161             pw.print("Running job");
    162             if (force) {
    163                 pw.print(" [FORCED]");
    164             }
    165             pw.println();
    166 
    167             return ret;
    168         } finally {
    169             Binder.restoreCallingIdentity(ident);
    170         }
    171     }
    172 
    173     private int timeout(PrintWriter pw) throws Exception {
    174         checkPermission("force timeout jobs");
    175 
    176         int userId = UserHandle.USER_ALL;
    177 
    178         String opt;
    179         while ((opt = getNextOption()) != null) {
    180             switch (opt) {
    181                 case "-u":
    182                 case "--user":
    183                     userId = UserHandle.parseUserArg(getNextArgRequired());
    184                     break;
    185 
    186                 default:
    187                     pw.println("Error: unknown option '" + opt + "'");
    188                     return -1;
    189             }
    190         }
    191 
    192         if (userId == UserHandle.USER_CURRENT) {
    193             userId = ActivityManager.getCurrentUser();
    194         }
    195 
    196         final String pkgName = getNextArg();
    197         final String jobIdStr = getNextArg();
    198         final int jobId = jobIdStr != null ? Integer.parseInt(jobIdStr) : -1;
    199 
    200         final long ident = Binder.clearCallingIdentity();
    201         try {
    202             return mInternal.executeTimeoutCommand(pw, pkgName, userId, jobIdStr != null, jobId);
    203         } finally {
    204             Binder.restoreCallingIdentity(ident);
    205         }
    206     }
    207 
    208     private int monitorBattery(PrintWriter pw) throws Exception {
    209         checkPermission("change battery monitoring");
    210         String opt = getNextArgRequired();
    211         boolean enabled;
    212         if ("on".equals(opt)) {
    213             enabled = true;
    214         } else if ("off".equals(opt)) {
    215             enabled = false;
    216         } else {
    217             getErrPrintWriter().println("Error: unknown option " + opt);
    218             return 1;
    219         }
    220         final long ident = Binder.clearCallingIdentity();
    221         try {
    222             mInternal.setMonitorBattery(enabled);
    223             if (enabled) pw.println("Battery monitoring enabled");
    224             else pw.println("Battery monitoring disabled");
    225         } finally {
    226             Binder.restoreCallingIdentity(ident);
    227         }
    228         return 0;
    229     }
    230 
    231     private int getBatterySeq(PrintWriter pw) {
    232         int seq = mInternal.getBatterySeq();
    233         pw.println(seq);
    234         return 0;
    235     }
    236 
    237     private int getBatteryCharging(PrintWriter pw) {
    238         boolean val = mInternal.getBatteryCharging();
    239         pw.println(val);
    240         return 0;
    241     }
    242 
    243     private int getBatteryNotLow(PrintWriter pw) {
    244         boolean val = mInternal.getBatteryNotLow();
    245         pw.println(val);
    246         return 0;
    247     }
    248 
    249     private int getStorageSeq(PrintWriter pw) {
    250         int seq = mInternal.getStorageSeq();
    251         pw.println(seq);
    252         return 0;
    253     }
    254 
    255     private int getStorageNotLow(PrintWriter pw) {
    256         boolean val = mInternal.getStorageNotLow();
    257         pw.println(val);
    258         return 0;
    259     }
    260 
    261     private int getJobState(PrintWriter pw) throws Exception {
    262         checkPermission("force timeout jobs");
    263 
    264         int userId = UserHandle.USER_SYSTEM;
    265 
    266         String opt;
    267         while ((opt = getNextOption()) != null) {
    268             switch (opt) {
    269                 case "-u":
    270                 case "--user":
    271                     userId = UserHandle.parseUserArg(getNextArgRequired());
    272                     break;
    273 
    274                 default:
    275                     pw.println("Error: unknown option '" + opt + "'");
    276                     return -1;
    277             }
    278         }
    279 
    280         if (userId == UserHandle.USER_CURRENT) {
    281             userId = ActivityManager.getCurrentUser();
    282         }
    283 
    284         final String pkgName = getNextArgRequired();
    285         final String jobIdStr = getNextArgRequired();
    286         final int jobId = Integer.parseInt(jobIdStr);
    287 
    288         final long ident = Binder.clearCallingIdentity();
    289         try {
    290             int ret = mInternal.getJobState(pw, pkgName, userId, jobId);
    291             printError(ret, pkgName, userId, jobId);
    292             return ret;
    293         } finally {
    294             Binder.restoreCallingIdentity(ident);
    295         }
    296     }
    297 
    298     @Override
    299     public void onHelp() {
    300         final PrintWriter pw = getOutPrintWriter();
    301 
    302         pw.println("Job scheduler (jobscheduler) commands:");
    303         pw.println("  help");
    304         pw.println("    Print this help text.");
    305         pw.println("  run [-f | --force] [-u | --user USER_ID] PACKAGE JOB_ID");
    306         pw.println("    Trigger immediate execution of a specific scheduled job.");
    307         pw.println("    Options:");
    308         pw.println("      -f or --force: run the job even if technical constraints such as");
    309         pw.println("         connectivity are not currently met");
    310         pw.println("      -u or --user: specify which user's job is to be run; the default is");
    311         pw.println("         the primary or system user");
    312         pw.println("  timeout [-u | --user USER_ID] [PACKAGE] [JOB_ID]");
    313         pw.println("    Trigger immediate timeout of currently executing jobs, as if their.");
    314         pw.println("    execution timeout had expired.");
    315         pw.println("    Options:");
    316         pw.println("      -u or --user: specify which user's job is to be run; the default is");
    317         pw.println("         all users");
    318         pw.println("  monitor-battery [on|off]");
    319         pw.println("    Control monitoring of all battery changes.  Off by default.  Turning");
    320         pw.println("    on makes get-battery-seq useful.");
    321         pw.println("  get-battery-seq");
    322         pw.println("    Return the last battery update sequence number that was received.");
    323         pw.println("  get-battery-charging");
    324         pw.println("    Return whether the battery is currently considered to be charging.");
    325         pw.println("  get-battery-not-low");
    326         pw.println("    Return whether the battery is currently considered to not be low.");
    327         pw.println("  get-storage-seq");
    328         pw.println("    Return the last storage update sequence number that was received.");
    329         pw.println("  get-storage-not-low");
    330         pw.println("    Return whether storage is currently considered to not be low.");
    331         pw.println("  get-job-state [-u | --user USER_ID] PACKAGE JOB_ID");
    332         pw.println("    Return the current state of a job, may be any combination of:");
    333         pw.println("      pending: currently on the pending list, waiting to be active");
    334         pw.println("      active: job is actively running");
    335         pw.println("      user-stopped: job can't run because its user is stopped");
    336         pw.println("      backing-up: job can't run because app is currently backing up its data");
    337         pw.println("      no-component: job can't run because its component is not available");
    338         pw.println("      ready: job is ready to run (all constraints satisfied or bypassed)");
    339         pw.println("      waiting: if nothing else above is printed, job not ready to run");
    340         pw.println("    Options:");
    341         pw.println("      -u or --user: specify which user's job is to be run; the default is");
    342         pw.println("         the primary or system user");
    343         pw.println();
    344     }
    345 
    346 }
    347