2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.server.job;
19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
22 import java.io.FileDescriptor;
23 import java.io.PrintWriter;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.Iterator;
29 import java.util.List;
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.app.AppGlobals;
34 import android.app.IUidObserver;
35 import android.app.job.JobInfo;
36 import android.app.job.JobParameters;
37 import android.app.job.JobScheduler;
38 import android.app.job.JobService;
39 import android.app.job.IJobScheduler;
40 import android.app.job.JobWorkItem;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.ContentResolver;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.IPackageManager;
48 import android.content.pm.PackageManager;
49 import android.content.pm.ServiceInfo;
50 import android.content.pm.PackageManager.NameNotFoundException;
51 import android.database.ContentObserver;
52 import android.net.Uri;
53 import android.os.BatteryStats;
54 import android.os.Binder;
55 import android.os.Handler;
56 import android.os.Looper;
57 import android.os.Message;
58 import android.os.Process;
59 import android.os.PowerManager;
60 import android.os.RemoteException;
61 import android.os.ResultReceiver;
62 import android.os.ServiceManager;
63 import android.os.ShellCallback;
64 import android.os.SystemClock;
65 import android.os.UserHandle;
66 import android.os.UserManagerInternal;
67 import android.provider.Settings;
68 import android.util.KeyValueListParser;
69 import android.util.Slog;
70 import android.util.SparseArray;
71 import android.util.SparseIntArray;
72 import android.util.TimeUtils;
74 import com.android.internal.app.IBatteryStats;
75 import com.android.internal.app.procstats.ProcessStats;
76 import com.android.internal.util.ArrayUtils;
77 import com.android.internal.util.DumpUtils;
78 import com.android.server.DeviceIdleController;
79 import com.android.server.FgThread;
80 import com.android.server.LocalServices;
81 import com.android.server.job.JobStore.JobStatusFunctor;
82 import com.android.server.job.controllers.AppIdleController;
83 import com.android.server.job.controllers.BatteryController;
84 import com.android.server.job.controllers.ConnectivityController;
85 import com.android.server.job.controllers.ContentObserverController;
86 import com.android.server.job.controllers.DeviceIdleJobsController;
87 import com.android.server.job.controllers.IdleController;
88 import com.android.server.job.controllers.JobStatus;
89 import com.android.server.job.controllers.StateController;
90 import com.android.server.job.controllers.StorageController;
91 import com.android.server.job.controllers.TimeController;
93 import libcore.util.EmptyArray;
96 * Responsible for taking jobs representing work to be performed by a client app, and determining
97 * based on the criteria specified when that job should be run against the client application's
99 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
100 * about constraints, or the state of active jobs. It receives callbacks from the various
101 * controllers and completed jobs and operates accordingly.
103 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
104 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
107 public final class JobSchedulerService extends com.android.server.SystemService
108 implements StateChangedListener, JobCompletedListener {
109 static final String TAG = "JobSchedulerService";
110 public static final boolean DEBUG = false;
112 /** The maximum number of concurrent jobs we run at one time. */
113 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
114 /** Enforce a per-app limit on scheduled jobs? */
115 private static final boolean ENFORCE_MAX_JOBS = true;
116 /** The maximum number of jobs that we allow an unprivileged app to schedule */
117 private static final int MAX_JOBS_PER_APP = 100;
120 /** Global local for all job scheduler state. */
121 final Object mLock = new Object();
122 /** Master list of jobs. */
123 final JobStore mJobs;
124 /** Tracking amount of time each package runs for. */
125 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
127 static final int MSG_JOB_EXPIRED = 0;
128 static final int MSG_CHECK_JOB = 1;
129 static final int MSG_STOP_JOB = 2;
130 static final int MSG_CHECK_JOB_GREEDY = 3;
133 * Track Services that have currently active or pending jobs. The index is provided by
134 * {@link JobStatus#getServiceToken()}
136 final List<JobServiceContext> mActiveServices = new ArrayList<>();
137 /** List of controllers that will notify this service of updates to jobs. */
138 List<StateController> mControllers;
139 /** Need direct access to this for testing. */
140 BatteryController mBatteryController;
141 /** Need direct access to this for testing. */
142 StorageController mStorageController;
144 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
145 * when ready to execute them.
147 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
149 int[] mStartedUsers = EmptyArray.INT;
151 final JobHandler mHandler;
152 final JobSchedulerStub mJobSchedulerStub;
154 IBatteryStats mBatteryStats;
155 PowerManager mPowerManager;
156 DeviceIdleController.LocalService mLocalDeviceIdleController;
159 * Set to true once we are allowed to run third party apps.
161 boolean mReadyToRock;
164 * What we last reported to DeviceIdleController about whether we are active.
166 boolean mReportedActive;
169 * Current limit on the number of concurrent JobServiceContext entries we want to
170 * keep actively running a job.
172 int mMaxActiveJobs = 1;
175 * Which uids are currently in the foreground.
177 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
180 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
182 final SparseIntArray mBackingUpUids = new SparseIntArray();
184 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
187 * This array essentially stores the state of mActiveServices array.
188 * The ith index stores the job present on the ith JobServiceContext.
189 * We manipulate this array until we arrive at what jobs should be running on
190 * what JobServiceContext.
192 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
194 * Indicates whether we need to act on this jobContext id
196 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
198 * The uid whose jobs we would like to assign to a context.
200 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
203 * All times are in milliseconds. These constants are kept synchronized with the system
204 * global Settings. Any access to this class or its fields should be done while
205 * holding the JobSchedulerService.mLock lock.
207 private final class Constants extends ContentObserver {
208 // Key names stored in the settings value.
209 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
210 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
211 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
212 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
213 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
214 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
215 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
216 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
217 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
218 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
219 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
220 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
221 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
222 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
223 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
224 = "max_standard_reschedule_count";
225 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
226 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
227 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
229 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
230 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
231 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
232 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
233 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
234 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
235 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
236 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
237 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
238 private static final int DEFAULT_FG_JOB_COUNT = 4;
239 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
240 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
241 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
242 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
243 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
244 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
245 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
246 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
249 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
252 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
254 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
257 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
259 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
260 * schedule things early.
262 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
264 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
265 * schedule things early.
267 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
269 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
270 * things early. 1 == Run connectivity jobs as soon as ready.
272 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
274 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
275 * schedule things early.
277 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
279 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
280 * running some work early. This (and thus the other min counts) is now set to 1, to
281 * prevent any batching at this level. Since we now do batching through doze, that is
282 * a much better mechanism.
284 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
286 * This is the job execution factor that is considered to be heavy use of the system.
288 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
290 * This is the job execution factor that is considered to be moderate use of the system.
292 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
294 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
296 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
298 * The maximum number of background jobs we allow when the system is in a normal
301 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
303 * The maximum number of background jobs we allow when the system is in a moderate
306 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
308 * The maximum number of background jobs we allow when the system is in a low
311 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
313 * The maximum number of background jobs we allow when the system is in a critical
316 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
318 * The maximum number of times we allow a job to have itself rescheduled before
319 * giving up on it, for standard jobs.
321 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
323 * The maximum number of times we allow a job to have itself rescheduled before
324 * giving up on it, for jobs that are executing work.
326 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
328 * The minimum backoff time to allow for linear backoff.
330 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
332 * The minimum backoff time to allow for exponential backoff.
334 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
336 private ContentResolver mResolver;
337 private final KeyValueListParser mParser = new KeyValueListParser(',');
339 public Constants(Handler handler) {
343 public void start(ContentResolver resolver) {
344 mResolver = resolver;
345 mResolver.registerContentObserver(Settings.Global.getUriFor(
346 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
351 public void onChange(boolean selfChange, Uri uri) {
355 private void updateConstants() {
356 synchronized (mLock) {
358 mParser.setString(Settings.Global.getString(mResolver,
359 Settings.Global.JOB_SCHEDULER_CONSTANTS));
360 } catch (IllegalArgumentException e) {
361 // Failed to parse the settings string, log this and move on
363 Slog.e(TAG, "Bad jobscheduler settings", e);
366 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
367 DEFAULT_MIN_IDLE_COUNT);
368 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
369 DEFAULT_MIN_CHARGING_COUNT);
370 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
371 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
372 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
373 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
374 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
375 DEFAULT_MIN_CONNECTIVITY_COUNT);
376 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
377 DEFAULT_MIN_CONTENT_COUNT);
378 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
379 DEFAULT_MIN_READY_JOBS_COUNT);
380 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
381 DEFAULT_HEAVY_USE_FACTOR);
382 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
383 DEFAULT_MODERATE_USE_FACTOR);
384 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
385 DEFAULT_FG_JOB_COUNT);
386 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
387 DEFAULT_BG_NORMAL_JOB_COUNT);
388 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
389 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
391 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
392 DEFAULT_BG_MODERATE_JOB_COUNT);
393 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
394 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
396 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
397 DEFAULT_BG_LOW_JOB_COUNT);
398 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
399 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
401 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
402 DEFAULT_BG_CRITICAL_JOB_COUNT);
403 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
404 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
406 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
407 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
408 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
409 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
410 MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
411 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
412 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
413 DEFAULT_MIN_EXP_BACKOFF_TIME);
417 void dump(PrintWriter pw) {
418 pw.println(" Settings:");
420 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
421 pw.print(MIN_IDLE_COUNT); pw.println();
423 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
424 pw.print(MIN_CHARGING_COUNT); pw.println();
426 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
427 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
429 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
430 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
432 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
433 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
435 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
436 pw.print(MIN_CONTENT_COUNT); pw.println();
438 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
439 pw.print(MIN_READY_JOBS_COUNT); pw.println();
441 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
442 pw.print(HEAVY_USE_FACTOR); pw.println();
444 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
445 pw.print(MODERATE_USE_FACTOR); pw.println();
447 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
448 pw.print(FG_JOB_COUNT); pw.println();
450 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
451 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
453 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
454 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
456 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
457 pw.print(BG_LOW_JOB_COUNT); pw.println();
459 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
460 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
462 pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
463 pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
465 pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
466 pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
468 pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
469 pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
471 pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
472 pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
476 final Constants mConstants;
478 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
479 if (o1.enqueueTime < o2.enqueueTime) {
482 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
485 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
486 int where = Collections.binarySearch(array, newItem, comparator);
490 array.add(where, newItem);
494 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
495 * still clean up. On reinstall the package will have a new uid.
497 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
499 public void onReceive(Context context, Intent intent) {
500 final String action = intent.getAction();
502 Slog.d(TAG, "Receieved: " + action);
504 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
505 // Purge the app's jobs if the whole package was just disabled. When this is
506 // the case the component name will be a bare package name.
507 final String pkgName = getPackageName(intent);
508 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
509 if (pkgName != null && pkgUid != -1) {
510 final String[] changedComponents = intent.getStringArrayExtra(
511 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
512 if (changedComponents != null) {
513 for (String component : changedComponents) {
514 if (component.equals(pkgName)) {
516 Slog.d(TAG, "Package state change: " + pkgName);
519 final int userId = UserHandle.getUserId(pkgUid);
520 IPackageManager pm = AppGlobals.getPackageManager();
521 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
522 if (state == COMPONENT_ENABLED_STATE_DISABLED
523 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
525 Slog.d(TAG, "Removing jobs for package " + pkgName
526 + " in user " + userId);
528 cancelJobsForUid(pkgUid, "app package state changed");
530 } catch (RemoteException|IllegalArgumentException e) {
532 * IllegalArgumentException means that the package doesn't exist.
533 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
534 * behind outright uninstall, so by the time we try to act it's gone.
535 * We don't need to act on this PACKAGE_CHANGED when this happens;
536 * we'll get a PACKAGE_REMOVED later and clean up then.
538 * RemoteException can't actually happen; the package manager is
539 * running in this same process.
547 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
549 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
550 // If this is an outright uninstall rather than the first half of an
551 // app update sequence, cancel the jobs associated with the app.
552 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
553 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
555 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
557 cancelJobsForUid(uidRemoved, "app uninstalled");
559 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
560 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
562 Slog.d(TAG, "Removing jobs for user: " + userId);
564 cancelJobsForUser(userId);
565 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
566 // Has this package scheduled any jobs, such that we will take action
567 // if it were to be force-stopped?
568 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
569 final String pkgName = intent.getData().getSchemeSpecificPart();
571 List<JobStatus> jobsForUid;
572 synchronized (mLock) {
573 jobsForUid = mJobs.getJobsByUid(pkgUid);
575 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
576 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
578 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
579 + pkgUid + " has jobs");
581 setResultCode(Activity.RESULT_OK);
586 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
587 // possible force-stop
588 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
589 final String pkgName = intent.getData().getSchemeSpecificPart();
592 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
594 cancelJobsForPackageAndUid(pkgName, pkgUid);
600 private String getPackageName(Intent intent) {
601 Uri uri = intent.getData();
602 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
606 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
607 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
608 updateUidState(uid, procState);
611 @Override public void onUidGone(int uid, boolean disabled) {
612 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
614 cancelJobsForUid(uid, "uid gone");
618 @Override public void onUidActive(int uid) throws RemoteException {
621 @Override public void onUidIdle(int uid, boolean disabled) {
623 cancelJobsForUid(uid, "app uid idle");
627 @Override public void onUidCachedChanged(int uid, boolean cached) {
631 public Object getLock() {
635 public JobStore getJobStore() {
640 public void onStartUser(int userHandle) {
641 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
642 // Let's kick any outstanding jobs for this user.
643 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
647 public void onUnlockUser(int userHandle) {
648 // Let's kick any outstanding jobs for this user.
649 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
653 public void onStopUser(int userHandle) {
654 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
657 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
658 int userId, String tag) {
660 if (ActivityManager.getService().isAppStartModeDisabled(uId,
661 job.getService().getPackageName())) {
662 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
663 + " -- package not allowed to start");
664 return JobScheduler.RESULT_FAILURE;
666 } catch (RemoteException e) {
668 synchronized (mLock) {
669 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
671 if (work != null && toCancel != null) {
672 // Fast path: we are adding work to an existing job, and the JobInfo is not
673 // changing. We can just directly enqueue this work in to the job.
674 if (toCancel.getJob().equals(job)) {
675 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
676 return JobScheduler.RESULT_SUCCESS;
680 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
681 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
682 // Jobs on behalf of others don't apply to the per-app job cap
683 if (ENFORCE_MAX_JOBS && packageName == null) {
684 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
685 Slog.w(TAG, "Too many jobs for uid " + uId);
686 throw new IllegalStateException("Apps may not schedule more than "
687 + MAX_JOBS_PER_APP + " distinct jobs");
691 // This may throw a SecurityException.
692 jobStatus.prepareLocked(ActivityManager.getService());
694 if (toCancel != null) {
695 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
698 // If work has been supplied, enqueue it into the new job.
699 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
701 startTrackingJobLocked(jobStatus, toCancel);
703 // If the job is immediately ready to run, then we can just immediately
704 // put it in the pending list and try to schedule it. This is especially
705 // important for jobs with a 0 deadline constraint, since they will happen a fair
706 // amount, we want to handle them as quickly as possible, and semantically we want to
707 // make sure we have started holding the wake lock for the job before returning to
709 // If the job is not yet ready to run, there is nothing more to do -- we are
710 // now just waiting for one of its controllers to change state and schedule
711 // the job appropriately.
712 if (isReadyToBeExecutedLocked(jobStatus)) {
713 // This is a new job, we can just immediately put it on the pending
714 // list and try to run it.
715 mJobPackageTracker.notePending(jobStatus);
716 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
717 maybeRunPendingJobsLocked();
720 return JobScheduler.RESULT_SUCCESS;
723 public List<JobInfo> getPendingJobs(int uid) {
724 synchronized (mLock) {
725 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
726 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
727 for (int i = jobs.size() - 1; i >= 0; i--) {
728 JobStatus job = jobs.get(i);
729 outList.add(job.getJob());
735 public JobInfo getPendingJob(int uid, int jobId) {
736 synchronized (mLock) {
737 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
738 for (int i = jobs.size() - 1; i >= 0; i--) {
739 JobStatus job = jobs.get(i);
740 if (job.getJobId() == jobId) {
748 void cancelJobsForUser(int userHandle) {
749 synchronized (mLock) {
750 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
751 for (int i=0; i<jobsForUser.size(); i++) {
752 JobStatus toRemove = jobsForUser.get(i);
753 cancelJobImplLocked(toRemove, null, "user removed");
758 private void cancelJobsForNonExistentUsers() {
759 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
760 synchronized (mLock) {
761 mJobs.removeJobsOfNonUsers(umi.getUserIds());
765 void cancelJobsForPackageAndUid(String pkgName, int uid) {
766 synchronized (mLock) {
767 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
768 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
769 final JobStatus job = jobsForUid.get(i);
770 if (job.getSourcePackageName().equals(pkgName)) {
771 cancelJobImplLocked(job, null, "app force stopped");
778 * Entry point from client to cancel all jobs originating from their uid.
779 * This will remove the job from the master list, and cancel the job if it was staged for
780 * execution or being executed.
781 * @param uid Uid to check against for removal of a job.
784 public void cancelJobsForUid(int uid, String reason) {
785 synchronized (mLock) {
786 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
787 for (int i=0; i<jobsForUid.size(); i++) {
788 JobStatus toRemove = jobsForUid.get(i);
789 cancelJobImplLocked(toRemove, null, reason);
795 * Entry point from client to cancel the job corresponding to the jobId provided.
796 * This will remove the job from the master list, and cancel the job if it was staged for
797 * execution or being executed.
798 * @param uid Uid of the calling client.
799 * @param jobId Id of the job, provided at schedule-time.
801 public void cancelJob(int uid, int jobId) {
803 synchronized (mLock) {
804 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
805 if (toCancel != null) {
806 cancelJobImplLocked(toCancel, null, "cancel() called by app");
811 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
812 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
813 cancelled.unprepareLocked(ActivityManager.getService());
814 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
815 // Remove from pending queue.
816 if (mPendingJobs.remove(cancelled)) {
817 mJobPackageTracker.noteNonpending(cancelled);
819 // Cancel if running.
820 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
821 reportActiveLocked();
824 void updateUidState(int uid, int procState) {
825 synchronized (mLock) {
826 if (procState == ActivityManager.PROCESS_STATE_TOP) {
827 // Only use this if we are exactly the top app. All others can live
828 // with just the foreground priority. This means that persistent processes
829 // can never be the top app priority... that is fine.
830 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
831 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
832 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
834 mUidPriorityOverride.delete(uid);
840 public void onDeviceIdleStateChanged(boolean deviceIdle) {
841 synchronized (mLock) {
843 // When becoming idle, make sure no jobs are actively running,
844 // except those using the idle exemption flag.
845 for (int i=0; i<mActiveServices.size(); i++) {
846 JobServiceContext jsc = mActiveServices.get(i);
847 final JobStatus executing = jsc.getRunningJobLocked();
848 if (executing != null
849 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
850 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
851 "cancelled due to doze");
855 // When coming out of idle, allow thing to start back up.
857 if (mLocalDeviceIdleController != null) {
858 if (!mReportedActive) {
859 mReportedActive = true;
860 mLocalDeviceIdleController.setJobsActive(true);
863 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
869 void reportActiveLocked() {
870 // active is true if pending queue contains jobs OR some job is running.
871 boolean active = mPendingJobs.size() > 0;
872 if (mPendingJobs.size() <= 0) {
873 for (int i=0; i<mActiveServices.size(); i++) {
874 final JobServiceContext jsc = mActiveServices.get(i);
875 final JobStatus job = jsc.getRunningJobLocked();
877 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
878 && !job.dozeWhitelisted) {
879 // We will report active if we have a job running and it is not an exception
880 // due to being in the foreground or whitelisted.
887 if (mReportedActive != active) {
888 mReportedActive = active;
889 if (mLocalDeviceIdleController != null) {
890 mLocalDeviceIdleController.setJobsActive(active);
896 * Initializes the system service.
898 * Subclasses must define a single argument constructor that accepts the context
899 * and passes it to super.
902 * @param context The system server context.
904 public JobSchedulerService(Context context) {
906 mHandler = new JobHandler(context.getMainLooper());
907 mConstants = new Constants(mHandler);
908 mJobSchedulerStub = new JobSchedulerStub();
909 mJobs = JobStore.initAndGet(this);
911 // Create the controllers.
912 mControllers = new ArrayList<StateController>();
913 mControllers.add(ConnectivityController.get(this));
914 mControllers.add(TimeController.get(this));
915 mControllers.add(IdleController.get(this));
916 mBatteryController = BatteryController.get(this);
917 mControllers.add(mBatteryController);
918 mStorageController = StorageController.get(this);
919 mControllers.add(mStorageController);
920 mControllers.add(AppIdleController.get(this));
921 mControllers.add(ContentObserverController.get(this));
922 mControllers.add(DeviceIdleJobsController.get(this));
924 // If the job store determined that it can't yet reschedule persisted jobs,
925 // we need to start watching the clock.
926 if (!mJobs.jobTimesInflatedValid()) {
927 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
928 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
932 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
934 public void onReceive(Context context, Intent intent) {
935 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
936 // When we reach clock sanity, recalculate the temporal windows
937 // of all affected jobs.
938 if (mJobs.clockNowValidToInflate(System.currentTimeMillis())) {
939 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
941 // We've done our job now, so stop watching the time.
942 context.unregisterReceiver(this);
944 // And kick off the work to update the affected jobs, using a secondary
945 // thread instead of chugging away here on the main looper thread.
946 FgThread.getHandler().post(mJobTimeUpdater);
952 private final Runnable mJobTimeUpdater = () -> {
953 final ArrayList<JobStatus> toRemove = new ArrayList<>();
954 final ArrayList<JobStatus> toAdd = new ArrayList<>();
955 synchronized (mLock) {
956 // Note: we intentionally both look up the existing affected jobs and replace them
957 // with recalculated ones inside the same lock lifetime.
958 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
960 // Now, at each position [i], we have both the existing JobStatus
961 // and the one that replaces it.
962 final int N = toAdd.size();
963 for (int i = 0; i < N; i++) {
964 final JobStatus oldJob = toRemove.get(i);
965 final JobStatus newJob = toAdd.get(i);
967 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
969 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
975 public void onStart() {
976 publishLocalService(JobSchedulerInternal.class, new LocalService());
977 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
981 public void onBootPhase(int phase) {
982 if (PHASE_SYSTEM_SERVICES_READY == phase) {
983 mConstants.start(getContext().getContentResolver());
984 // Register br for package removals and user removals.
985 final IntentFilter filter = new IntentFilter();
986 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
987 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
988 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
989 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
990 filter.addDataScheme("package");
991 getContext().registerReceiverAsUser(
992 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
993 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
994 getContext().registerReceiverAsUser(
995 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
996 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
998 ActivityManager.getService().registerUidObserver(mUidObserver,
999 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
1000 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
1002 } catch (RemoteException e) {
1003 // ignored; both services live in system_server
1005 // Remove any jobs that are not associated with any of the current users.
1006 cancelJobsForNonExistentUsers();
1007 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
1008 synchronized (mLock) {
1010 mReadyToRock = true;
1011 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1012 BatteryStats.SERVICE_NAME));
1013 mLocalDeviceIdleController
1014 = LocalServices.getService(DeviceIdleController.LocalService.class);
1015 // Create the "runners".
1016 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1017 mActiveServices.add(
1018 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
1019 getContext().getMainLooper()));
1021 // Attach jobs to their controllers.
1022 mJobs.forEachJob(new JobStatusFunctor() {
1024 public void process(JobStatus job) {
1025 for (int controller = 0; controller < mControllers.size(); controller++) {
1026 final StateController sc = mControllers.get(controller);
1027 sc.maybeStartTrackingJobLocked(job, null);
1032 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1038 * Called when we have a job status object that we need to insert in our
1039 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1042 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1043 if (!jobStatus.isPreparedLocked()) {
1044 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1046 jobStatus.enqueueTime = SystemClock.elapsedRealtime();
1047 final boolean update = mJobs.add(jobStatus);
1049 for (int i = 0; i < mControllers.size(); i++) {
1050 StateController controller = mControllers.get(i);
1052 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1054 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1060 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1063 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1064 boolean writeBack) {
1065 // Deal with any remaining work items in the old job.
1066 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
1068 // Remove from store as well as controllers.
1069 final boolean removed = mJobs.remove(jobStatus, writeBack);
1070 if (removed && mReadyToRock) {
1071 for (int i=0; i<mControllers.size(); i++) {
1072 StateController controller = mControllers.get(i);
1073 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1079 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
1080 for (int i=0; i<mActiveServices.size(); i++) {
1081 JobServiceContext jsc = mActiveServices.get(i);
1082 final JobStatus executing = jsc.getRunningJobLocked();
1083 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1084 jsc.cancelExecutingJobLocked(reason, debugReason);
1092 * @param job JobStatus we are querying against.
1093 * @return Whether or not the job represented by the status object is currently being run or
1096 private boolean isCurrentlyActiveLocked(JobStatus job) {
1097 for (int i=0; i<mActiveServices.size(); i++) {
1098 JobServiceContext serviceContext = mActiveServices.get(i);
1099 final JobStatus running = serviceContext.getRunningJobLocked();
1100 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1107 void noteJobsPending(List<JobStatus> jobs) {
1108 for (int i = jobs.size() - 1; i >= 0; i--) {
1109 JobStatus job = jobs.get(i);
1110 mJobPackageTracker.notePending(job);
1114 void noteJobsNonpending(List<JobStatus> jobs) {
1115 for (int i = jobs.size() - 1; i >= 0; i--) {
1116 JobStatus job = jobs.get(i);
1117 mJobPackageTracker.noteNonpending(job);
1122 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1123 * specify an override deadline on a failed job (the failed job will run even though it's not
1124 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1125 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1127 * @param failureToReschedule Provided job status that we will reschedule.
1128 * @return A newly instantiated JobStatus with the same constraints as the last job except
1129 * with adjusted timing constraints.
1131 * @see #maybeQueueReadyJobsForExecutionLocked
1133 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1134 final long elapsedNowMillis = SystemClock.elapsedRealtime();
1135 final JobInfo job = failureToReschedule.getJob();
1137 final long initialBackoffMillis = job.getInitialBackoffMillis();
1138 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1141 if (failureToReschedule.hasWorkLocked()) {
1142 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1143 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1144 + backoffAttempts + " > work limit "
1145 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1148 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1149 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1150 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1154 switch (job.getBackoffPolicy()) {
1155 case JobInfo.BACKOFF_POLICY_LINEAR: {
1156 long backoff = initialBackoffMillis;
1157 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1158 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1160 delayMillis = backoff * backoffAttempts;
1164 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1166 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1167 long backoff = initialBackoffMillis;
1168 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1169 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1171 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1175 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1176 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
1177 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
1178 failureToReschedule.getLastSuccessfulRunTime(), System.currentTimeMillis());
1179 for (int ic=0; ic<mControllers.size(); ic++) {
1180 StateController controller = mControllers.get(ic);
1181 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1187 * Called after a periodic has executed so we can reschedule it. We take the last execution
1188 * time of the job to be the time of completion (i.e. the time at which this function is
1190 * This could be inaccurate b/c the job can run for as long as
1191 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1192 * to underscheduling at least, rather than if we had taken the last execution time to be the
1193 * start of the execution.
1194 * @return A new job representing the execution criteria for this instantiation of the
1197 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1198 final long elapsedNow = SystemClock.elapsedRealtime();
1199 // Compute how much of the period is remaining.
1202 // If this periodic was rescheduled it won't have a deadline.
1203 if (periodicToReschedule.hasDeadlineConstraint()) {
1204 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1206 long flex = periodicToReschedule.getJob().getFlexMillis();
1207 long period = periodicToReschedule.getJob().getIntervalMillis();
1208 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1209 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1212 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1213 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1215 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1216 newLatestRuntimeElapsed, 0 /* backoffAttempt */,
1217 System.currentTimeMillis() /* lastSuccessfulRunTime */,
1218 periodicToReschedule.getLastFailedRunTime());
1221 // JobCompletedListener implementations.
1224 * A job just finished executing. We fetch the
1225 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1226 * whether we want to reschedule we readd it to the controllers.
1227 * @param jobStatus Completed job.
1228 * @param needsReschedule Whether the implementing class should reschedule this job.
1231 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1233 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1236 // If the job wants to be rescheduled, we first need to make the next upcoming
1237 // job so we can transfer any appropriate state over from the previous job when
1239 final JobStatus rescheduledJob = needsReschedule
1240 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1242 // Do not write back immediately if this is a periodic job. The job may get lost if system
1243 // shuts down before it is added back.
1244 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1246 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1248 // We still want to check for jobs to execute, because this job may have
1249 // scheduled a new job under the same job id, and now we can run it.
1250 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1254 if (rescheduledJob != null) {
1256 rescheduledJob.prepareLocked(ActivityManager.getService());
1257 } catch (SecurityException e) {
1258 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1260 startTrackingJobLocked(rescheduledJob, jobStatus);
1261 } else if (jobStatus.getJob().isPeriodic()) {
1262 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1264 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1265 } catch (SecurityException e) {
1266 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1268 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1270 jobStatus.unprepareLocked(ActivityManager.getService());
1271 reportActiveLocked();
1272 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1275 // StateChangedListener implementations.
1278 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1279 * some controller's state has changed, so as to run through the list of jobs and start/stop
1280 * any that are eligible.
1283 public void onControllerStateChanged() {
1284 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1288 public void onRunJobNow(JobStatus jobStatus) {
1289 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1292 final private class JobHandler extends Handler {
1294 public JobHandler(Looper looper) {
1299 public void handleMessage(Message message) {
1300 synchronized (mLock) {
1301 if (!mReadyToRock) {
1304 switch (message.what) {
1305 case MSG_JOB_EXPIRED: {
1306 JobStatus runNow = (JobStatus) message.obj;
1307 // runNow can be null, which is a controller's way of indicating that its
1308 // state is such that all ready jobs should be run immediately.
1309 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
1310 mJobPackageTracker.notePending(runNow);
1311 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
1313 queueReadyJobsForExecutionLocked();
1317 if (mReportedActive) {
1318 // if jobs are currently being run, queue all ready jobs for execution.
1319 queueReadyJobsForExecutionLocked();
1321 // Check the list of jobs and run some of them if we feel inclined.
1322 maybeQueueReadyJobsForExecutionLocked();
1325 case MSG_CHECK_JOB_GREEDY:
1326 queueReadyJobsForExecutionLocked();
1329 cancelJobImplLocked((JobStatus) message.obj, null,
1330 "app no longer allowed to run");
1333 maybeRunPendingJobsLocked();
1334 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1335 removeMessages(MSG_CHECK_JOB);
1340 private void stopNonReadyActiveJobsLocked() {
1341 for (int i=0; i<mActiveServices.size(); i++) {
1342 JobServiceContext serviceContext = mActiveServices.get(i);
1343 final JobStatus running = serviceContext.getRunningJobLocked();
1344 if (running != null && !running.isReady()) {
1345 serviceContext.cancelExecutingJobLocked(
1346 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1347 "cancelled due to unsatisfied constraints");
1353 * Run through list of jobs and execute all possible - at least one is expired so we do
1354 * as many as we can.
1356 private void queueReadyJobsForExecutionLocked() {
1358 Slog.d(TAG, "queuing all ready jobs for execution:");
1360 noteJobsNonpending(mPendingJobs);
1361 mPendingJobs.clear();
1362 stopNonReadyActiveJobsLocked();
1363 mJobs.forEachJob(mReadyQueueFunctor);
1364 mReadyQueueFunctor.postProcess();
1367 final int queuedJobs = mPendingJobs.size();
1368 if (queuedJobs == 0) {
1369 Slog.d(TAG, "No jobs pending.");
1371 Slog.d(TAG, queuedJobs + " jobs queued.");
1376 final class ReadyJobQueueFunctor implements JobStatusFunctor {
1377 ArrayList<JobStatus> newReadyJobs;
1380 public void process(JobStatus job) {
1381 if (isReadyToBeExecutedLocked(job)) {
1383 Slog.d(TAG, " queued " + job.toShortString());
1385 if (newReadyJobs == null) {
1386 newReadyJobs = new ArrayList<JobStatus>();
1388 newReadyJobs.add(job);
1392 public void postProcess() {
1393 if (newReadyJobs != null) {
1394 noteJobsPending(newReadyJobs);
1395 mPendingJobs.addAll(newReadyJobs);
1396 if (mPendingJobs.size() > 1) {
1397 mPendingJobs.sort(mEnqueueTimeComparator);
1400 newReadyJobs = null;
1403 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1406 * The state of at least one job has changed. Here is where we could enforce various
1407 * policies on when we want to execute jobs.
1408 * Right now the policy is such:
1409 * If >1 of the ready jobs is idle mode we send all of them off
1410 * if more than 2 network connectivity jobs are ready we send them all off.
1411 * If more than 4 jobs total are ready we send them all off.
1412 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1414 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1416 int batteryNotLowCount;
1417 int storageNotLowCount;
1420 int connectivityCount;
1422 List<JobStatus> runnableJobs;
1424 public MaybeReadyJobQueueFunctor() {
1428 // Functor method invoked for each job via JobStore.forEachJob()
1430 public void process(JobStatus job) {
1431 if (isReadyToBeExecutedLocked(job)) {
1433 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1434 job.getJob().getService().getPackageName())) {
1435 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1436 + job.getJob().toString() + " -- package not allowed to start");
1437 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1440 } catch (RemoteException e) {
1442 if (job.getNumFailures() > 0) {
1445 if (job.hasIdleConstraint()) {
1448 if (job.hasConnectivityConstraint()) {
1449 connectivityCount++;
1451 if (job.hasChargingConstraint()) {
1454 if (job.hasBatteryNotLowConstraint()) {
1455 batteryNotLowCount++;
1457 if (job.hasStorageNotLowConstraint()) {
1458 storageNotLowCount++;
1460 if (job.hasContentTriggerConstraint()) {
1463 if (runnableJobs == null) {
1464 runnableJobs = new ArrayList<>();
1466 runnableJobs.add(job);
1470 public void postProcess() {
1471 if (backoffCount > 0 ||
1472 idleCount >= mConstants.MIN_IDLE_COUNT ||
1473 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1474 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1475 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1476 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1477 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1478 (runnableJobs != null
1479 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1481 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1483 noteJobsPending(runnableJobs);
1484 mPendingJobs.addAll(runnableJobs);
1485 if (mPendingJobs.size() > 1) {
1486 mPendingJobs.sort(mEnqueueTimeComparator);
1490 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1494 // Be ready for next time
1498 private void reset() {
1502 connectivityCount = 0;
1503 batteryNotLowCount = 0;
1504 storageNotLowCount = 0;
1506 runnableJobs = null;
1509 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1511 private void maybeQueueReadyJobsForExecutionLocked() {
1512 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1514 noteJobsNonpending(mPendingJobs);
1515 mPendingJobs.clear();
1516 stopNonReadyActiveJobsLocked();
1517 mJobs.forEachJob(mMaybeQueueFunctor);
1518 mMaybeQueueFunctor.postProcess();
1522 * Criteria for moving a job into the pending queue:
1524 * - It's not pending.
1525 * - It's not already running on a JSC.
1526 * - The user that requested the job is running.
1527 * - The component is enabled and runnable.
1529 private boolean isReadyToBeExecutedLocked(JobStatus job) {
1530 final boolean jobReady = job.isReady();
1533 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1534 + " ready=" + jobReady);
1537 // This is a condition that is very likely to be false (most jobs that are
1538 // scheduled are sitting there, not ready yet) and very cheap to check (just
1539 // a few conditions on data in JobStatus).
1544 final boolean jobExists = mJobs.containsJob(job);
1546 final int userId = job.getUserId();
1547 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1550 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1551 + " exists=" + jobExists + " userStarted=" + userStarted);
1554 // These are also fairly cheap to check, though they typically will not
1555 // be conditions we fail.
1556 if (!jobExists || !userStarted) {
1560 final boolean jobPending = mPendingJobs.contains(job);
1561 final boolean jobActive = isCurrentlyActiveLocked(job);
1564 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1565 + " pending=" + jobPending + " active=" + jobActive);
1568 // These can be a little more expensive (especially jobActive, since we need to
1569 // go through the array of all potentially active jobs), so we are doing them
1570 // later... but still before checking with the package manager!
1571 if (jobPending || jobActive) {
1575 final boolean componentPresent;
1577 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1578 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1580 } catch (RemoteException e) {
1581 throw e.rethrowAsRuntimeException();
1585 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1586 + " componentPresent=" + componentPresent);
1589 // Everything else checked out so far, so this is the final yes/no check
1590 return componentPresent;
1594 * Reconcile jobs in the pending queue against available execution contexts.
1595 * A controller can force a job into the pending queue even if it's already running, but
1596 * here is where we decide whether to actually execute it.
1598 private void maybeRunPendingJobsLocked() {
1600 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1602 assignJobsToContextsLocked();
1603 reportActiveLocked();
1606 private int adjustJobPriority(int curPriority, JobStatus job) {
1607 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1608 float factor = mJobPackageTracker.getLoadFactor(job);
1609 if (factor >= mConstants.HEAVY_USE_FACTOR) {
1610 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
1611 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
1612 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1618 private int evaluateJobPriorityLocked(JobStatus job) {
1619 int priority = job.getPriority();
1620 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1621 return adjustJobPriority(priority, job);
1623 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1624 if (override != 0) {
1625 return adjustJobPriority(override, job);
1627 return adjustJobPriority(priority, job);
1631 * Takes jobs from pending queue and runs them on available contexts.
1632 * If no contexts are available, preempts lower priority jobs to
1633 * run higher priority ones.
1634 * Lock on mJobs before calling this function.
1636 private void assignJobsToContextsLocked() {
1638 Slog.d(TAG, printPendingQueue());
1643 memLevel = ActivityManager.getService().getMemoryTrimLevel();
1644 } catch (RemoteException e) {
1645 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1648 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1649 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1651 case ProcessStats.ADJ_MEM_FACTOR_LOW:
1652 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1654 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1655 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1658 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1662 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1663 boolean[] act = mTmpAssignAct;
1664 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1666 int numForeground = 0;
1667 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1668 final JobServiceContext js = mActiveServices.get(i);
1669 final JobStatus status = js.getRunningJobLocked();
1670 if ((contextIdToJobMap[i] = status) != null) {
1672 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1677 preferredUidForContext[i] = js.getPreferredUid();
1680 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1682 for (int i=0; i<mPendingJobs.size(); i++) {
1683 JobStatus nextPending = mPendingJobs.get(i);
1685 // If job is already running, go to next job.
1686 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1687 if (jobRunningContext != -1) {
1691 final int priority = evaluateJobPriorityLocked(nextPending);
1692 nextPending.lastEvaluatedPriority = priority;
1694 // Find a context for nextPending. The context should be available OR
1695 // it should have lowest priority among all running jobs
1696 // (sharing the same Uid as nextPending)
1697 int minPriority = Integer.MAX_VALUE;
1698 int minPriorityContextId = -1;
1699 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1700 JobStatus job = contextIdToJobMap[j];
1701 int preferredUid = preferredUidForContext[j];
1703 if ((numActive < mMaxActiveJobs ||
1704 (priority >= JobInfo.PRIORITY_TOP_APP &&
1705 numForeground < mConstants.FG_JOB_COUNT)) &&
1706 (preferredUid == nextPending.getUid() ||
1707 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1708 // This slot is free, and we haven't yet hit the limit on
1709 // concurrent jobs... we can just throw the job in to here.
1710 minPriorityContextId = j;
1713 // No job on this context, but nextPending can't run here because
1714 // the context has a preferred Uid or we have reached the limit on
1718 if (job.getUid() != nextPending.getUid()) {
1721 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1724 if (minPriority > nextPending.lastEvaluatedPriority) {
1725 minPriority = nextPending.lastEvaluatedPriority;
1726 minPriorityContextId = j;
1729 if (minPriorityContextId != -1) {
1730 contextIdToJobMap[minPriorityContextId] = nextPending;
1731 act[minPriorityContextId] = true;
1733 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1739 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1741 mJobPackageTracker.noteConcurrency(numActive, numForeground);
1742 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1743 boolean preservePreferredUid = false;
1745 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
1748 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
1750 // preferredUid will be set to uid of currently running job.
1751 mActiveServices.get(i).preemptExecutingJobLocked();
1752 preservePreferredUid = true;
1754 final JobStatus pendingJob = contextIdToJobMap[i];
1756 Slog.d(TAG, "About to run job on context "
1757 + String.valueOf(i) + ", job: " + pendingJob);
1759 for (int ic=0; ic<mControllers.size(); ic++) {
1760 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1762 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1763 Slog.d(TAG, "Error executing " + pendingJob);
1765 if (mPendingJobs.remove(pendingJob)) {
1766 mJobPackageTracker.noteNonpending(pendingJob);
1770 if (!preservePreferredUid) {
1771 mActiveServices.get(i).clearPreferredUid();
1776 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1777 for (int i=0; i<map.length; i++) {
1778 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1785 final class LocalService implements JobSchedulerInternal {
1788 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1789 * jobs are always considered pending.
1792 public List<JobInfo> getSystemScheduledPendingJobs() {
1793 synchronized (mLock) {
1794 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1795 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1797 public void process(JobStatus job) {
1798 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1799 pendingJobs.add(job.getJob());
1808 public void addBackingUpUid(int uid) {
1809 synchronized (mLock) {
1810 // No need to actually do anything here, since for a full backup the
1811 // activity manager will kill the process which will kill the job (and
1812 // cause it to restart, but now it can't run).
1813 mBackingUpUids.put(uid, uid);
1818 public void removeBackingUpUid(int uid) {
1819 synchronized (mLock) {
1820 mBackingUpUids.delete(uid);
1821 // If there are any jobs for this uid, we need to rebuild the pending list
1822 // in case they are now ready to run.
1823 if (mJobs.countJobsForUid(uid) > 0) {
1824 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1830 public void clearAllBackingUpUids() {
1831 synchronized (mLock) {
1832 if (mBackingUpUids.size() > 0) {
1833 mBackingUpUids.clear();
1834 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1841 * Binder stub trampoline implementation
1843 final class JobSchedulerStub extends IJobScheduler.Stub {
1844 /** Cache determination of whether a given app can persist jobs
1845 * key is uid of the calling app; value is undetermined/true/false
1847 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1849 // Enforce that only the app itself (or shared uid participant) can schedule a
1850 // job that runs one of the app's services, as well as verifying that the
1851 // named service properly requires the BIND_JOB_SERVICE permission
1852 private void enforceValidJobRequest(int uid, JobInfo job) {
1853 final IPackageManager pm = AppGlobals.getPackageManager();
1854 final ComponentName service = job.getService();
1856 ServiceInfo si = pm.getServiceInfo(service,
1857 PackageManager.MATCH_DIRECT_BOOT_AWARE
1858 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1859 UserHandle.getUserId(uid));
1861 throw new IllegalArgumentException("No such service " + service);
1863 if (si.applicationInfo.uid != uid) {
1864 throw new IllegalArgumentException("uid " + uid +
1865 " cannot schedule job in " + service.getPackageName());
1867 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1868 throw new IllegalArgumentException("Scheduled service " + service
1869 + " does not require android.permission.BIND_JOB_SERVICE permission");
1871 } catch (RemoteException e) {
1872 // Can't happen; the Package Manager is in this same process
1876 private boolean canPersistJobs(int pid, int uid) {
1877 // If we get this far we're good to go; all we need to do now is check
1878 // whether the app is allowed to persist its scheduled work.
1879 final boolean canPersist;
1880 synchronized (mPersistCache) {
1881 Boolean cached = mPersistCache.get(uid);
1882 if (cached != null) {
1883 canPersist = cached.booleanValue();
1885 // Persisting jobs is tantamount to running at boot, so we permit
1886 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1888 int result = getContext().checkPermission(
1889 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1890 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1891 mPersistCache.put(uid, canPersist);
1897 // IJobScheduler implementation
1899 public int schedule(JobInfo job) throws RemoteException {
1901 Slog.d(TAG, "Scheduling job: " + job.toString());
1903 final int pid = Binder.getCallingPid();
1904 final int uid = Binder.getCallingUid();
1906 enforceValidJobRequest(uid, job);
1907 if (job.isPersisted()) {
1908 if (!canPersistJobs(pid, uid)) {
1909 throw new IllegalArgumentException("Error: requested job be persisted without"
1910 + " holding RECEIVE_BOOT_COMPLETED permission.");
1914 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1915 getContext().enforceCallingOrSelfPermission(
1916 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1919 long ident = Binder.clearCallingIdentity();
1921 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1923 Binder.restoreCallingIdentity(ident);
1927 // IJobScheduler implementation
1929 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1931 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1933 final int pid = Binder.getCallingPid();
1934 final int uid = Binder.getCallingUid();
1936 enforceValidJobRequest(uid, job);
1937 if (job.isPersisted()) {
1938 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1941 throw new NullPointerException("work is null");
1944 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1945 getContext().enforceCallingOrSelfPermission(
1946 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1949 long ident = Binder.clearCallingIdentity();
1951 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
1953 Binder.restoreCallingIdentity(ident);
1958 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1959 throws RemoteException {
1960 final int callerUid = Binder.getCallingUid();
1962 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1963 + " on behalf of " + packageName);
1966 if (packageName == null) {
1967 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1970 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1971 android.Manifest.permission.UPDATE_DEVICE_STATS);
1972 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1973 throw new SecurityException("Caller uid " + callerUid
1974 + " not permitted to schedule jobs for other apps");
1977 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1978 getContext().enforceCallingOrSelfPermission(
1979 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1982 long ident = Binder.clearCallingIdentity();
1984 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
1985 packageName, userId, tag);
1987 Binder.restoreCallingIdentity(ident);
1992 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1993 final int uid = Binder.getCallingUid();
1995 long ident = Binder.clearCallingIdentity();
1997 return JobSchedulerService.this.getPendingJobs(uid);
1999 Binder.restoreCallingIdentity(ident);
2004 public JobInfo getPendingJob(int jobId) throws RemoteException {
2005 final int uid = Binder.getCallingUid();
2007 long ident = Binder.clearCallingIdentity();
2009 return JobSchedulerService.this.getPendingJob(uid, jobId);
2011 Binder.restoreCallingIdentity(ident);
2016 public void cancelAll() throws RemoteException {
2017 final int uid = Binder.getCallingUid();
2019 long ident = Binder.clearCallingIdentity();
2021 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
2023 Binder.restoreCallingIdentity(ident);
2028 public void cancel(int jobId) throws RemoteException {
2029 final int uid = Binder.getCallingUid();
2031 long ident = Binder.clearCallingIdentity();
2033 JobSchedulerService.this.cancelJob(uid, jobId);
2035 Binder.restoreCallingIdentity(ident);
2040 * "dumpsys" infrastructure
2043 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2044 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2046 long identityToken = Binder.clearCallingIdentity();
2048 JobSchedulerService.this.dumpInternal(pw, args);
2050 Binder.restoreCallingIdentity(identityToken);
2055 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2056 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
2057 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2058 this, in, out, err, args, callback, resultReceiver);
2062 // Shell command infrastructure: run the given job immediately
2063 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2065 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2066 + " " + jobId + " f=" + force);
2070 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2071 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2073 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2076 synchronized (mLock) {
2077 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2079 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2082 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2083 if (!js.isConstraintsSatisfied()) {
2084 js.overrideState = 0;
2085 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2088 queueReadyJobsForExecutionLocked();
2089 maybeRunPendingJobsLocked();
2091 } catch (RemoteException e) {
2097 // Shell command infrastructure: immediately timeout currently executing jobs
2098 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2099 boolean hasJobId, int jobId) {
2101 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2104 synchronized (mLock) {
2105 boolean foundSome = false;
2106 for (int i=0; i<mActiveServices.size(); i++) {
2107 final JobServiceContext jc = mActiveServices.get(i);
2108 final JobStatus js = jc.getRunningJobLocked();
2109 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId)) {
2111 pw.print("Timing out: ");
2112 js.printUniqueId(pw);
2114 pw.println(js.getServiceComponent().flattenToShortString());
2118 pw.println("No matching executing jobs found.");
2124 void setMonitorBattery(boolean enabled) {
2125 synchronized (mLock) {
2126 if (mBatteryController != null) {
2127 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2132 int getBatterySeq() {
2133 synchronized (mLock) {
2134 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2138 boolean getBatteryCharging() {
2139 synchronized (mLock) {
2140 return mBatteryController != null
2141 ? mBatteryController.getTracker().isOnStablePower() : false;
2145 boolean getBatteryNotLow() {
2146 synchronized (mLock) {
2147 return mBatteryController != null
2148 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2152 int getStorageSeq() {
2153 synchronized (mLock) {
2154 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2158 boolean getStorageNotLow() {
2159 synchronized (mLock) {
2160 return mStorageController != null
2161 ? mStorageController.getTracker().isStorageNotLow() : false;
2165 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2167 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2168 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2170 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2171 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2174 synchronized (mLock) {
2175 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2176 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2178 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2179 pw.print("/jid"); pw.print(jobId); pw.println(")");
2180 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2183 boolean printed = false;
2184 if (mPendingJobs.contains(js)) {
2185 pw.print("pending");
2188 if (isCurrentlyActiveLocked(js)) {
2193 pw.println("active");
2195 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2200 pw.println("user-stopped");
2202 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2207 pw.println("backing-up");
2209 boolean componentPresent = false;
2211 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2212 js.getServiceComponent(),
2213 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2214 js.getUserId()) != null);
2215 } catch (RemoteException e) {
2217 if (!componentPresent) {
2222 pw.println("no-component");
2229 pw.println("ready");
2232 pw.print("waiting");
2236 } catch (RemoteException e) {
2242 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2243 StringBuilder s = new StringBuilder(initial + ": ");
2244 for (int i=0; i<map.length; i++) {
2246 .append(map[i] == null? -1: map[i].getJobId())
2247 .append(map[i] == null? -1: map[i].getUid())
2250 return s.toString();
2253 private String printPendingQueue() {
2254 StringBuilder s = new StringBuilder("Pending queue: ");
2255 Iterator<JobStatus> it = mPendingJobs.iterator();
2256 while (it.hasNext()) {
2257 JobStatus js = it.next();
2259 .append(js.getJob().getId())
2261 .append(js.getUid())
2264 return s.toString();
2267 static void dumpHelp(PrintWriter pw) {
2268 pw.println("Job Scheduler (jobscheduler) dump options:");
2269 pw.println(" [-h] [package] ...");
2270 pw.println(" -h: print this help");
2271 pw.println(" [package] is an optional package name to limit the output to.");
2274 void dumpInternal(final PrintWriter pw, String[] args) {
2276 if (!ArrayUtils.isEmpty(args)) {
2278 while (opti < args.length) {
2279 String arg = args[opti];
2280 if ("-h".equals(arg)) {
2283 } else if ("-a".equals(arg)) {
2284 // Ignore, we always dump all.
2285 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2286 pw.println("Unknown option: " + arg);
2293 if (opti < args.length) {
2294 String pkg = args[opti];
2296 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2297 PackageManager.MATCH_ANY_USER);
2298 } catch (NameNotFoundException ignored) {
2299 pw.println("Invalid package: " + pkg);
2305 final int filterUidFinal = UserHandle.getAppId(filterUid);
2306 final long nowElapsed = SystemClock.elapsedRealtime();
2307 final long nowUptime = SystemClock.uptimeMillis();
2308 synchronized (mLock) {
2309 mConstants.dump(pw);
2311 pw.println("Started users: " + Arrays.toString(mStartedUsers));
2312 pw.print("Registered ");
2313 pw.print(mJobs.size());
2314 pw.println(" jobs:");
2315 if (mJobs.size() > 0) {
2316 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2317 Collections.sort(jobs, new Comparator<JobStatus>() {
2319 public int compare(JobStatus o1, JobStatus o2) {
2320 int uid1 = o1.getUid();
2321 int uid2 = o2.getUid();
2322 int id1 = o1.getJobId();
2323 int id2 = o2.getJobId();
2325 return uid1 < uid2 ? -1 : 1;
2327 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
2330 for (JobStatus job : jobs) {
2331 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2332 pw.println(job.toShortStringExceptUniqueId());
2334 // Skip printing details if the caller requested a filter
2335 if (!job.shouldDump(filterUidFinal)) {
2339 job.dump(pw, " ", true, nowElapsed);
2340 pw.print(" Ready: ");
2341 pw.print(isReadyToBeExecutedLocked(job));
2343 pw.print(job.isReady());
2345 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
2346 pw.print(" !pending=");
2347 pw.print(!mPendingJobs.contains(job));
2348 pw.print(" !active=");
2349 pw.print(!isCurrentlyActiveLocked(job));
2350 pw.print(" !backingup=");
2351 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
2353 boolean componentPresent = false;
2355 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2356 job.getServiceComponent(),
2357 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2358 job.getUserId()) != null);
2359 } catch (RemoteException e) {
2361 pw.print(componentPresent);
2365 pw.println(" None.");
2367 for (int i=0; i<mControllers.size(); i++) {
2369 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
2372 pw.println("Uid priority overrides:");
2373 for (int i=0; i< mUidPriorityOverride.size(); i++) {
2374 int uid = mUidPriorityOverride.keyAt(i);
2375 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2376 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2377 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2380 if (mBackingUpUids.size() > 0) {
2382 pw.println("Backing up uids:");
2383 boolean first = true;
2384 for (int i = 0; i < mBackingUpUids.size(); i++) {
2385 int uid = mBackingUpUids.keyAt(i);
2386 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2393 pw.print(UserHandle.formatUid(uid));
2399 mJobPackageTracker.dump(pw, "", filterUidFinal);
2401 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2404 pw.println("Pending queue:");
2405 for (int i=0; i<mPendingJobs.size(); i++) {
2406 JobStatus job = mPendingJobs.get(i);
2407 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2408 pw.println(job.toShortString());
2409 job.dump(pw, " ", false, nowElapsed);
2410 int priority = evaluateJobPriorityLocked(job);
2411 if (priority != JobInfo.PRIORITY_DEFAULT) {
2412 pw.print(" Evaluated priority: "); pw.println(priority);
2414 pw.print(" Tag: "); pw.println(job.getTag());
2416 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
2420 pw.println("Active jobs:");
2421 for (int i=0; i<mActiveServices.size(); i++) {
2422 JobServiceContext jsc = mActiveServices.get(i);
2423 pw.print(" Slot #"); pw.print(i); pw.print(": ");
2424 final JobStatus job = jsc.getRunningJobLocked();
2426 if (jsc.mStoppedReason != null) {
2427 pw.print("inactive since ");
2428 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
2429 pw.print(", stopped because: ");
2430 pw.println(jsc.mStoppedReason);
2432 pw.println("inactive");
2436 pw.println(job.toShortString());
2437 pw.print(" Running for: ");
2438 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
2439 pw.print(", timeout at: ");
2440 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
2442 job.dump(pw, " ", false, nowElapsed);
2443 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
2444 if (priority != JobInfo.PRIORITY_DEFAULT) {
2445 pw.print(" Evaluated priority: "); pw.println(priority);
2447 pw.print(" Active at ");
2448 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
2449 pw.print(", pending for ");
2450 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2454 if (filterUid == -1) {
2456 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2457 pw.print("mReportedActive="); pw.println(mReportedActive);
2458 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);