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 JobSchedulerShellCommand(JobSchedulerService service)37 JobSchedulerShellCommand(JobSchedulerService service) { 38 mInternal = service; 39 mPM = AppGlobals.getPackageManager(); 40 } 41 42 @Override onCommand(String cmd)43 public int onCommand(String cmd) { 44 final PrintWriter pw = getOutPrintWriter(); 45 try { 46 switch (cmd != null ? cmd : "") { 47 case "run": 48 return runJob(pw); 49 case "timeout": 50 return timeout(pw); 51 case "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 checkPermission(String operation)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 printError(int errCode, String pkgName, int userId, int jobId)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 runJob(PrintWriter pw)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 timeout(PrintWriter pw)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 monitorBattery(PrintWriter pw)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 getBatterySeq(PrintWriter pw)231 private int getBatterySeq(PrintWriter pw) { 232 int seq = mInternal.getBatterySeq(); 233 pw.println(seq); 234 return 0; 235 } 236 getBatteryCharging(PrintWriter pw)237 private int getBatteryCharging(PrintWriter pw) { 238 boolean val = mInternal.getBatteryCharging(); 239 pw.println(val); 240 return 0; 241 } 242 getBatteryNotLow(PrintWriter pw)243 private int getBatteryNotLow(PrintWriter pw) { 244 boolean val = mInternal.getBatteryNotLow(); 245 pw.println(val); 246 return 0; 247 } 248 getStorageSeq(PrintWriter pw)249 private int getStorageSeq(PrintWriter pw) { 250 int seq = mInternal.getStorageSeq(); 251 pw.println(seq); 252 return 0; 253 } 254 getStorageNotLow(PrintWriter pw)255 private int getStorageNotLow(PrintWriter pw) { 256 boolean val = mInternal.getStorageNotLow(); 257 pw.println(val); 258 return 0; 259 } 260 getJobState(PrintWriter pw)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 onHelp()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