OSDN Git Service

Merge "Cherry-pick: Add null check for Wi-Fi command" into oc-dr1-dev
[android-x86/frameworks-base.git] / services / core / java / com / android / server / job / JobSchedulerService.java
1 /*
2  * Copyright (C) 2014 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 static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
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;
30
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;
73
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;
92
93 import libcore.util.EmptyArray;
94
95 /**
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
98  * endpoint.
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.
102  *
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}.
105  * @hide
106  */
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;
111
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;
118
119
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();
126
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;
131
132     /**
133      * Track Services that have currently active or pending jobs. The index is provided by
134      * {@link JobStatus#getServiceToken()}
135      */
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;
143     /**
144      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
145      * when ready to execute them.
146      */
147     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
148
149     int[] mStartedUsers = EmptyArray.INT;
150
151     final JobHandler mHandler;
152     final JobSchedulerStub mJobSchedulerStub;
153
154     IBatteryStats mBatteryStats;
155     PowerManager mPowerManager;
156     DeviceIdleController.LocalService mLocalDeviceIdleController;
157
158     /**
159      * Set to true once we are allowed to run third party apps.
160      */
161     boolean mReadyToRock;
162
163     /**
164      * What we last reported to DeviceIdleController about whether we are active.
165      */
166     boolean mReportedActive;
167
168     /**
169      * Current limit on the number of concurrent JobServiceContext entries we want to
170      * keep actively running a job.
171      */
172     int mMaxActiveJobs = 1;
173
174     /**
175      * Which uids are currently in the foreground.
176      */
177     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
178
179     /**
180      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
181      */
182     final SparseIntArray mBackingUpUids = new SparseIntArray();
183
184     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
185
186     /**
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.
191      */
192     JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
193     /**
194      * Indicates whether we need to act on this jobContext id
195      */
196     boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
197     /**
198      * The uid whose jobs we would like to assign to a context.
199      */
200     int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
201
202     /**
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.
206      */
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";
228
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;
247
248         /**
249          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
250          * early.
251          */
252         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
253         /**
254          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
255          * things early.
256          */
257         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
258         /**
259          * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
260          * schedule things early.
261          */
262         int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
263         /**
264          * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
265          * schedule things early.
266          */
267         int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
268         /**
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.
271          */
272         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
273         /**
274          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
275          * schedule things early.
276          */
277         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
278         /**
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.
283          */
284         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
285         /**
286          * This is the job execution factor that is considered to be heavy use of the system.
287          */
288         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
289         /**
290          * This is the job execution factor that is considered to be moderate use of the system.
291          */
292         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
293         /**
294          * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
295          */
296         int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
297         /**
298          * The maximum number of background jobs we allow when the system is in a normal
299          * memory state.
300          */
301         int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
302         /**
303          * The maximum number of background jobs we allow when the system is in a moderate
304          * memory state.
305          */
306         int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
307         /**
308          * The maximum number of background jobs we allow when the system is in a low
309          * memory state.
310          */
311         int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
312         /**
313          * The maximum number of background jobs we allow when the system is in a critical
314          * memory state.
315          */
316         int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
317         /**
318          * The maximum number of times we allow a job to have itself rescheduled before
319          * giving up on it, for standard jobs.
320          */
321         int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
322         /**
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.
325          */
326         int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
327         /**
328          * The minimum backoff time to allow for linear backoff.
329          */
330         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
331         /**
332          * The minimum backoff time to allow for exponential backoff.
333          */
334         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
335
336         private ContentResolver mResolver;
337         private final KeyValueListParser mParser = new KeyValueListParser(',');
338
339         public Constants(Handler handler) {
340             super(handler);
341         }
342
343         public void start(ContentResolver resolver) {
344             mResolver = resolver;
345             mResolver.registerContentObserver(Settings.Global.getUriFor(
346                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
347             updateConstants();
348         }
349
350         @Override
351         public void onChange(boolean selfChange, Uri uri) {
352             updateConstants();
353         }
354
355         private void updateConstants() {
356             synchronized (mLock) {
357                 try {
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
362                     // with defaults.
363                     Slog.e(TAG, "Bad jobscheduler settings", e);
364                 }
365
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;
390                 }
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;
395                 }
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;
400                 }
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;
405                 }
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);
414             }
415         }
416
417         void dump(PrintWriter pw) {
418             pw.println("  Settings:");
419
420             pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
421             pw.print(MIN_IDLE_COUNT); pw.println();
422
423             pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
424             pw.print(MIN_CHARGING_COUNT); pw.println();
425
426             pw.print("    "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
427             pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
428
429             pw.print("    "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
430             pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
431
432             pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
433             pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
434
435             pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
436             pw.print(MIN_CONTENT_COUNT); pw.println();
437
438             pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
439             pw.print(MIN_READY_JOBS_COUNT); pw.println();
440
441             pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
442             pw.print(HEAVY_USE_FACTOR); pw.println();
443
444             pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
445             pw.print(MODERATE_USE_FACTOR); pw.println();
446
447             pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
448             pw.print(FG_JOB_COUNT); pw.println();
449
450             pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
451             pw.print(BG_NORMAL_JOB_COUNT); pw.println();
452
453             pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
454             pw.print(BG_MODERATE_JOB_COUNT); pw.println();
455
456             pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
457             pw.print(BG_LOW_JOB_COUNT); pw.println();
458
459             pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
460             pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
461
462             pw.print("    "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
463             pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
464
465             pw.print("    "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
466             pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
467
468             pw.print("    "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
469             pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
470
471             pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
472             pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
473         }
474     }
475
476     final Constants mConstants;
477
478     static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
479         if (o1.enqueueTime < o2.enqueueTime) {
480             return -1;
481         }
482         return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
483     };
484
485     static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
486         int where = Collections.binarySearch(array, newItem, comparator);
487         if (where < 0) {
488             where = ~where;
489         }
490         array.add(where, newItem);
491     }
492
493     /**
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.
496      */
497     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
498         @Override
499         public void onReceive(Context context, Intent intent) {
500             final String action = intent.getAction();
501             if (DEBUG) {
502                 Slog.d(TAG, "Receieved: " + action);
503             }
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)) {
515                                 if (DEBUG) {
516                                     Slog.d(TAG, "Package state change: " + pkgName);
517                                 }
518                                 try {
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) {
524                                         if (DEBUG) {
525                                             Slog.d(TAG, "Removing jobs for package " + pkgName
526                                                     + " in user " + userId);
527                                         }
528                                         cancelJobsForUid(pkgUid, "app package state changed");
529                                     }
530                                 } catch (RemoteException|IllegalArgumentException e) {
531                                     /*
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.
537                                      *
538                                      * RemoteException can't actually happen; the package manager is
539                                      * running in this same process.
540                                      */
541                                 }
542                                 break;
543                             }
544                         }
545                     }
546                 } else {
547                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
548                 }
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);
554                     if (DEBUG) {
555                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
556                     }
557                     cancelJobsForUid(uidRemoved, "app uninstalled");
558                 }
559             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
560                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
561                 if (DEBUG) {
562                     Slog.d(TAG, "Removing jobs for user: " + userId);
563                 }
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();
570                 if (pkgUid != -1) {
571                     List<JobStatus> jobsForUid;
572                     synchronized (mLock) {
573                         jobsForUid = mJobs.getJobsByUid(pkgUid);
574                     }
575                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
576                         if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
577                             if (DEBUG) {
578                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
579                                         + pkgUid + " has jobs");
580                             }
581                             setResultCode(Activity.RESULT_OK);
582                             break;
583                         }
584                     }
585                 }
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();
590                 if (pkgUid != -1) {
591                     if (DEBUG) {
592                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
593                     }
594                     cancelJobsForPackageAndUid(pkgName, pkgUid);
595                 }
596             }
597         }
598     };
599
600     private String getPackageName(Intent intent) {
601         Uri uri = intent.getData();
602         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
603         return pkg;
604     }
605
606     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
607         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
608             updateUidState(uid, procState);
609         }
610
611         @Override public void onUidGone(int uid, boolean disabled) {
612             updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
613             if (disabled) {
614                 cancelJobsForUid(uid, "uid gone");
615             }
616         }
617
618         @Override public void onUidActive(int uid) throws RemoteException {
619         }
620
621         @Override public void onUidIdle(int uid, boolean disabled) {
622             if (disabled) {
623                 cancelJobsForUid(uid, "app uid idle");
624             }
625         }
626
627         @Override public void onUidCachedChanged(int uid, boolean cached) {
628         }
629     };
630
631     public Object getLock() {
632         return mLock;
633     }
634
635     public JobStore getJobStore() {
636         return mJobs;
637     }
638
639     @Override
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();
644     }
645
646     @Override
647     public void onUnlockUser(int userHandle) {
648         // Let's kick any outstanding jobs for this user.
649         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
650     }
651
652     @Override
653     public void onStopUser(int userHandle) {
654         mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
655     }
656
657     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
658             int userId, String tag) {
659         try {
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;
665             }
666         } catch (RemoteException e) {
667         }
668         synchronized (mLock) {
669             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
670
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;
677                 }
678             }
679
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");
688                 }
689             }
690
691             // This may throw a SecurityException.
692             jobStatus.prepareLocked(ActivityManager.getService());
693
694             if (toCancel != null) {
695                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
696             }
697             if (work != null) {
698                 // If work has been supplied, enqueue it into the new job.
699                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
700             }
701             startTrackingJobLocked(jobStatus, toCancel);
702
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
708             // the caller.
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();
718             }
719         }
720         return JobScheduler.RESULT_SUCCESS;
721     }
722
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());
730             }
731             return outList;
732         }
733     }
734
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) {
741                     return job.getJob();
742                 }
743             }
744             return null;
745         }
746     }
747
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");
754             }
755         }
756     }
757
758     private void cancelJobsForNonExistentUsers() {
759         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
760         synchronized (mLock) {
761             mJobs.removeJobsOfNonUsers(umi.getUserIds());
762         }
763     }
764
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");
772                 }
773             }
774         }
775     }
776
777     /**
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.
782      *
783      */
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);
790             }
791         }
792     }
793
794     /**
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.
800      */
801     public void cancelJob(int uid, int jobId) {
802         JobStatus toCancel;
803         synchronized (mLock) {
804             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
805             if (toCancel != null) {
806                 cancelJobImplLocked(toCancel, null, "cancel() called by app");
807             }
808         }
809     }
810
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);
818         }
819         // Cancel if running.
820         stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
821         reportActiveLocked();
822     }
823
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);
833             } else {
834                 mUidPriorityOverride.delete(uid);
835             }
836         }
837     }
838
839     @Override
840     public void onDeviceIdleStateChanged(boolean deviceIdle) {
841         synchronized (mLock) {
842             if (deviceIdle) {
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");
852                     }
853                 }
854             } else {
855                 // When coming out of idle, allow thing to start back up.
856                 if (mReadyToRock) {
857                     if (mLocalDeviceIdleController != null) {
858                         if (!mReportedActive) {
859                             mReportedActive = true;
860                             mLocalDeviceIdleController.setJobsActive(true);
861                         }
862                     }
863                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
864                 }
865             }
866         }
867     }
868
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();
876                 if (job != null
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.
881                     active = true;
882                     break;
883                 }
884             }
885         }
886
887         if (mReportedActive != active) {
888             mReportedActive = active;
889             if (mLocalDeviceIdleController != null) {
890                 mLocalDeviceIdleController.setJobsActive(active);
891             }
892         }
893     }
894
895     /**
896      * Initializes the system service.
897      * <p>
898      * Subclasses must define a single argument constructor that accepts the context
899      * and passes it to super.
900      * </p>
901      *
902      * @param context The system server context.
903      */
904     public JobSchedulerService(Context context) {
905         super(context);
906         mHandler = new JobHandler(context.getMainLooper());
907         mConstants = new Constants(mHandler);
908         mJobSchedulerStub = new JobSchedulerStub();
909         mJobs = JobStore.initAndGet(this);
910
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));
923
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));
929         }
930     }
931
932     private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
933         @Override
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");
940
941                     // We've done our job now, so stop watching the time.
942                     context.unregisterReceiver(this);
943
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);
947                 }
948             }
949         }
950     };
951
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);
959
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);
966                 if (DEBUG) {
967                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
968                 }
969                 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
970             }
971         }
972     };
973
974     @Override
975     public void onStart() {
976         publishLocalService(JobSchedulerInternal.class, new LocalService());
977         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
978     }
979
980     @Override
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);
997             try {
998                 ActivityManager.getService().registerUidObserver(mUidObserver,
999                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
1000                         | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
1001                         null);
1002             } catch (RemoteException e) {
1003                 // ignored; both services live in system_server
1004             }
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) {
1009                 // Let's go!
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()));
1020                 }
1021                 // Attach jobs to their controllers.
1022                 mJobs.forEachJob(new JobStatusFunctor() {
1023                     @Override
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);
1028                         }
1029                     }
1030                 });
1031                 // GO GO GO!
1032                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1033             }
1034         }
1035     }
1036
1037     /**
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
1040      * about.
1041      */
1042     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1043         if (!jobStatus.isPreparedLocked()) {
1044             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1045         }
1046         jobStatus.enqueueTime = SystemClock.elapsedRealtime();
1047         final boolean update = mJobs.add(jobStatus);
1048         if (mReadyToRock) {
1049             for (int i = 0; i < mControllers.size(); i++) {
1050                 StateController controller = mControllers.get(i);
1051                 if (update) {
1052                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1053                 }
1054                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1055             }
1056         }
1057     }
1058
1059     /**
1060      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1061      * object removed.
1062      */
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);
1067
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);
1074             }
1075         }
1076         return removed;
1077     }
1078
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);
1085                 return true;
1086             }
1087         }
1088         return false;
1089     }
1090
1091     /**
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
1094      * is pending.
1095      */
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())) {
1101                 return true;
1102             }
1103         }
1104         return false;
1105     }
1106
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);
1111         }
1112     }
1113
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);
1118         }
1119     }
1120
1121     /**
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.
1126      *
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.
1130      *
1131      * @see #maybeQueueReadyJobsForExecutionLocked
1132      */
1133     private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1134         final long elapsedNowMillis = SystemClock.elapsedRealtime();
1135         final JobInfo job = failureToReschedule.getJob();
1136
1137         final long initialBackoffMillis = job.getInitialBackoffMillis();
1138         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1139         long delayMillis;
1140
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);
1146                 return null;
1147             }
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);
1151             return null;
1152         }
1153
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;
1159                 }
1160                 delayMillis = backoff * backoffAttempts;
1161             } break;
1162             default:
1163                 if (DEBUG) {
1164                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1165                 }
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;
1170                 }
1171                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1172             } break;
1173         }
1174         delayMillis =
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);
1182         }
1183         return newJob;
1184     }
1185
1186     /**
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
1189      * called).
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
1195      * recurring job.
1196      */
1197     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1198         final long elapsedNow = SystemClock.elapsedRealtime();
1199         // Compute how much of the period is remaining.
1200         long runEarly = 0L;
1201
1202         // If this periodic was rescheduled it won't have a deadline.
1203         if (periodicToReschedule.hasDeadlineConstraint()) {
1204             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1205         }
1206         long flex = periodicToReschedule.getJob().getFlexMillis();
1207         long period = periodicToReschedule.getJob().getIntervalMillis();
1208         long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1209         long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1210
1211         if (DEBUG) {
1212             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1213                     newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1214         }
1215         return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1216                 newLatestRuntimeElapsed, 0 /* backoffAttempt */,
1217                 System.currentTimeMillis() /* lastSuccessfulRunTime */,
1218                 periodicToReschedule.getLastFailedRunTime());
1219     }
1220
1221     // JobCompletedListener implementations.
1222
1223     /**
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.
1229      */
1230     @Override
1231     public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1232         if (DEBUG) {
1233             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1234         }
1235
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
1238         // we stop it.
1239         final JobStatus rescheduledJob = needsReschedule
1240                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1241
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())) {
1245             if (DEBUG) {
1246                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1247             }
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();
1251             return;
1252         }
1253
1254         if (rescheduledJob != null) {
1255             try {
1256                 rescheduledJob.prepareLocked(ActivityManager.getService());
1257             } catch (SecurityException e) {
1258                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1259             }
1260             startTrackingJobLocked(rescheduledJob, jobStatus);
1261         } else if (jobStatus.getJob().isPeriodic()) {
1262             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1263             try {
1264                 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1265             } catch (SecurityException e) {
1266                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1267             }
1268             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1269         }
1270         jobStatus.unprepareLocked(ActivityManager.getService());
1271         reportActiveLocked();
1272         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1273     }
1274
1275     // StateChangedListener implementations.
1276
1277     /**
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.
1281      */
1282     @Override
1283     public void onControllerStateChanged() {
1284         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1285     }
1286
1287     @Override
1288     public void onRunJobNow(JobStatus jobStatus) {
1289         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1290     }
1291
1292     final private class JobHandler extends Handler {
1293
1294         public JobHandler(Looper looper) {
1295             super(looper);
1296         }
1297
1298         @Override
1299         public void handleMessage(Message message) {
1300             synchronized (mLock) {
1301                 if (!mReadyToRock) {
1302                     return;
1303                 }
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);
1312                         } else {
1313                             queueReadyJobsForExecutionLocked();
1314                         }
1315                     } break;
1316                     case MSG_CHECK_JOB:
1317                         if (mReportedActive) {
1318                             // if jobs are currently being run, queue all ready jobs for execution.
1319                             queueReadyJobsForExecutionLocked();
1320                         } else {
1321                             // Check the list of jobs and run some of them if we feel inclined.
1322                             maybeQueueReadyJobsForExecutionLocked();
1323                         }
1324                         break;
1325                     case MSG_CHECK_JOB_GREEDY:
1326                         queueReadyJobsForExecutionLocked();
1327                         break;
1328                     case MSG_STOP_JOB:
1329                         cancelJobImplLocked((JobStatus) message.obj, null,
1330                                 "app no longer allowed to run");
1331                         break;
1332                 }
1333                 maybeRunPendingJobsLocked();
1334                 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1335                 removeMessages(MSG_CHECK_JOB);
1336             }
1337         }
1338     }
1339
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");
1348             }
1349         }
1350     }
1351
1352     /**
1353      * Run through list of jobs and execute all possible - at least one is expired so we do
1354      * as many as we can.
1355      */
1356     private void queueReadyJobsForExecutionLocked() {
1357         if (DEBUG) {
1358             Slog.d(TAG, "queuing all ready jobs for execution:");
1359         }
1360         noteJobsNonpending(mPendingJobs);
1361         mPendingJobs.clear();
1362         stopNonReadyActiveJobsLocked();
1363         mJobs.forEachJob(mReadyQueueFunctor);
1364         mReadyQueueFunctor.postProcess();
1365
1366         if (DEBUG) {
1367             final int queuedJobs = mPendingJobs.size();
1368             if (queuedJobs == 0) {
1369                 Slog.d(TAG, "No jobs pending.");
1370             } else {
1371                 Slog.d(TAG, queuedJobs + " jobs queued.");
1372             }
1373         }
1374     }
1375
1376     final class ReadyJobQueueFunctor implements JobStatusFunctor {
1377         ArrayList<JobStatus> newReadyJobs;
1378
1379         @Override
1380         public void process(JobStatus job) {
1381             if (isReadyToBeExecutedLocked(job)) {
1382                 if (DEBUG) {
1383                     Slog.d(TAG, "    queued " + job.toShortString());
1384                 }
1385                 if (newReadyJobs == null) {
1386                     newReadyJobs = new ArrayList<JobStatus>();
1387                 }
1388                 newReadyJobs.add(job);
1389             }
1390         }
1391
1392         public void postProcess() {
1393             if (newReadyJobs != null) {
1394                 noteJobsPending(newReadyJobs);
1395                 mPendingJobs.addAll(newReadyJobs);
1396                 if (mPendingJobs.size() > 1) {
1397                     mPendingJobs.sort(mEnqueueTimeComparator);
1398                 }
1399             }
1400             newReadyJobs = null;
1401         }
1402     }
1403     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1404
1405     /**
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.
1413      */
1414     final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1415         int chargingCount;
1416         int batteryNotLowCount;
1417         int storageNotLowCount;
1418         int idleCount;
1419         int backoffCount;
1420         int connectivityCount;
1421         int contentCount;
1422         List<JobStatus> runnableJobs;
1423
1424         public MaybeReadyJobQueueFunctor() {
1425             reset();
1426         }
1427
1428         // Functor method invoked for each job via JobStore.forEachJob()
1429         @Override
1430         public void process(JobStatus job) {
1431             if (isReadyToBeExecutedLocked(job)) {
1432                 try {
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();
1438                         return;
1439                     }
1440                 } catch (RemoteException e) {
1441                 }
1442                 if (job.getNumFailures() > 0) {
1443                     backoffCount++;
1444                 }
1445                 if (job.hasIdleConstraint()) {
1446                     idleCount++;
1447                 }
1448                 if (job.hasConnectivityConstraint()) {
1449                     connectivityCount++;
1450                 }
1451                 if (job.hasChargingConstraint()) {
1452                     chargingCount++;
1453                 }
1454                 if (job.hasBatteryNotLowConstraint()) {
1455                     batteryNotLowCount++;
1456                 }
1457                 if (job.hasStorageNotLowConstraint()) {
1458                     storageNotLowCount++;
1459                 }
1460                 if (job.hasContentTriggerConstraint()) {
1461                     contentCount++;
1462                 }
1463                 if (runnableJobs == null) {
1464                     runnableJobs = new ArrayList<>();
1465                 }
1466                 runnableJobs.add(job);
1467             }
1468         }
1469
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)) {
1480                 if (DEBUG) {
1481                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1482                 }
1483                 noteJobsPending(runnableJobs);
1484                 mPendingJobs.addAll(runnableJobs);
1485                 if (mPendingJobs.size() > 1) {
1486                     mPendingJobs.sort(mEnqueueTimeComparator);
1487                 }
1488             } else {
1489                 if (DEBUG) {
1490                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1491                 }
1492             }
1493
1494             // Be ready for next time
1495             reset();
1496         }
1497
1498         private void reset() {
1499             chargingCount = 0;
1500             idleCount =  0;
1501             backoffCount = 0;
1502             connectivityCount = 0;
1503             batteryNotLowCount = 0;
1504             storageNotLowCount = 0;
1505             contentCount = 0;
1506             runnableJobs = null;
1507         }
1508     }
1509     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1510
1511     private void maybeQueueReadyJobsForExecutionLocked() {
1512         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1513
1514         noteJobsNonpending(mPendingJobs);
1515         mPendingJobs.clear();
1516         stopNonReadyActiveJobsLocked();
1517         mJobs.forEachJob(mMaybeQueueFunctor);
1518         mMaybeQueueFunctor.postProcess();
1519     }
1520
1521     /**
1522      * Criteria for moving a job into the pending queue:
1523      *      - It's ready.
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.
1528      */
1529     private boolean isReadyToBeExecutedLocked(JobStatus job) {
1530         final boolean jobReady = job.isReady();
1531
1532         if (DEBUG) {
1533             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1534                     + " ready=" + jobReady);
1535         }
1536
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).
1540         if (!jobReady) {
1541             return false;
1542         }
1543
1544         final boolean jobExists = mJobs.containsJob(job);
1545
1546         final int userId = job.getUserId();
1547         final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1548
1549         if (DEBUG) {
1550             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1551                     + " exists=" + jobExists + " userStarted=" + userStarted);
1552         }
1553
1554         // These are also fairly cheap to check, though they typically will not
1555         // be conditions we fail.
1556         if (!jobExists || !userStarted) {
1557             return false;
1558         }
1559
1560         final boolean jobPending = mPendingJobs.contains(job);
1561         final boolean jobActive = isCurrentlyActiveLocked(job);
1562
1563         if (DEBUG) {
1564             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1565                     + " pending=" + jobPending + " active=" + jobActive);
1566         }
1567
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) {
1572             return false;
1573         }
1574
1575         final boolean componentPresent;
1576         try {
1577             componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1578                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1579                     userId) != null);
1580         } catch (RemoteException e) {
1581             throw e.rethrowAsRuntimeException();
1582         }
1583
1584         if (DEBUG) {
1585             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1586                     + " componentPresent=" + componentPresent);
1587         }
1588
1589         // Everything else checked out so far, so this is the final yes/no check
1590         return componentPresent;
1591     }
1592
1593     /**
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.
1597      */
1598     private void maybeRunPendingJobsLocked() {
1599         if (DEBUG) {
1600             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1601         }
1602         assignJobsToContextsLocked();
1603         reportActiveLocked();
1604     }
1605
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;
1613             }
1614         }
1615         return curPriority;
1616     }
1617
1618     private int evaluateJobPriorityLocked(JobStatus job) {
1619         int priority = job.getPriority();
1620         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1621             return adjustJobPriority(priority, job);
1622         }
1623         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1624         if (override != 0) {
1625             return adjustJobPriority(override, job);
1626         }
1627         return adjustJobPriority(priority, job);
1628     }
1629
1630     /**
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.
1635      */
1636     private void assignJobsToContextsLocked() {
1637         if (DEBUG) {
1638             Slog.d(TAG, printPendingQueue());
1639         }
1640
1641         int memLevel;
1642         try {
1643             memLevel = ActivityManager.getService().getMemoryTrimLevel();
1644         } catch (RemoteException e) {
1645             memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1646         }
1647         switch (memLevel) {
1648             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1649                 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1650                 break;
1651             case ProcessStats.ADJ_MEM_FACTOR_LOW:
1652                 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1653                 break;
1654             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1655                 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1656                 break;
1657             default:
1658                 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1659                 break;
1660         }
1661
1662         JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1663         boolean[] act = mTmpAssignAct;
1664         int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1665         int numActive = 0;
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) {
1671                 numActive++;
1672                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1673                     numForeground++;
1674                 }
1675             }
1676             act[i] = false;
1677             preferredUidForContext[i] = js.getPreferredUid();
1678         }
1679         if (DEBUG) {
1680             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1681         }
1682         for (int i=0; i<mPendingJobs.size(); i++) {
1683             JobStatus nextPending = mPendingJobs.get(i);
1684
1685             // If job is already running, go to next job.
1686             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1687             if (jobRunningContext != -1) {
1688                 continue;
1689             }
1690
1691             final int priority = evaluateJobPriorityLocked(nextPending);
1692             nextPending.lastEvaluatedPriority = priority;
1693
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];
1702                 if (job == null) {
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;
1711                         break;
1712                     }
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
1715                     // concurrent jobs.
1716                     continue;
1717                 }
1718                 if (job.getUid() != nextPending.getUid()) {
1719                     continue;
1720                 }
1721                 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1722                     continue;
1723                 }
1724                 if (minPriority > nextPending.lastEvaluatedPriority) {
1725                     minPriority = nextPending.lastEvaluatedPriority;
1726                     minPriorityContextId = j;
1727                 }
1728             }
1729             if (minPriorityContextId != -1) {
1730                 contextIdToJobMap[minPriorityContextId] = nextPending;
1731                 act[minPriorityContextId] = true;
1732                 numActive++;
1733                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1734                     numForeground++;
1735                 }
1736             }
1737         }
1738         if (DEBUG) {
1739             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1740         }
1741         mJobPackageTracker.noteConcurrency(numActive, numForeground);
1742         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1743             boolean preservePreferredUid = false;
1744             if (act[i]) {
1745                 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
1746                 if (js != null) {
1747                     if (DEBUG) {
1748                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
1749                     }
1750                     // preferredUid will be set to uid of currently running job.
1751                     mActiveServices.get(i).preemptExecutingJobLocked();
1752                     preservePreferredUid = true;
1753                 } else {
1754                     final JobStatus pendingJob = contextIdToJobMap[i];
1755                     if (DEBUG) {
1756                         Slog.d(TAG, "About to run job on context "
1757                                 + String.valueOf(i) + ", job: " + pendingJob);
1758                     }
1759                     for (int ic=0; ic<mControllers.size(); ic++) {
1760                         mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1761                     }
1762                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1763                         Slog.d(TAG, "Error executing " + pendingJob);
1764                     }
1765                     if (mPendingJobs.remove(pendingJob)) {
1766                         mJobPackageTracker.noteNonpending(pendingJob);
1767                     }
1768                 }
1769             }
1770             if (!preservePreferredUid) {
1771                 mActiveServices.get(i).clearPreferredUid();
1772             }
1773         }
1774     }
1775
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())) {
1779                 return i;
1780             }
1781         }
1782         return -1;
1783     }
1784
1785     final class LocalService implements JobSchedulerInternal {
1786
1787         /**
1788          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1789          * jobs are always considered pending.
1790          */
1791         @Override
1792         public List<JobInfo> getSystemScheduledPendingJobs() {
1793             synchronized (mLock) {
1794                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1795                 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1796                     @Override
1797                     public void process(JobStatus job) {
1798                         if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1799                             pendingJobs.add(job.getJob());
1800                         }
1801                     }
1802                 });
1803                 return pendingJobs;
1804             }
1805         }
1806
1807         @Override
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);
1814             }
1815         }
1816
1817         @Override
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();
1825                 }
1826             }
1827         }
1828
1829         @Override
1830         public void clearAllBackingUpUids() {
1831             synchronized (mLock) {
1832                 if (mBackingUpUids.size() > 0) {
1833                     mBackingUpUids.clear();
1834                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1835                 }
1836             }
1837         }
1838     }
1839
1840     /**
1841      * Binder stub trampoline implementation
1842      */
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
1846          */
1847         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1848
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();
1855             try {
1856                 ServiceInfo si = pm.getServiceInfo(service,
1857                         PackageManager.MATCH_DIRECT_BOOT_AWARE
1858                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1859                         UserHandle.getUserId(uid));
1860                 if (si == null) {
1861                     throw new IllegalArgumentException("No such service " + service);
1862                 }
1863                 if (si.applicationInfo.uid != uid) {
1864                     throw new IllegalArgumentException("uid " + uid +
1865                             " cannot schedule job in " + service.getPackageName());
1866                 }
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");
1870                 }
1871             } catch (RemoteException e) {
1872                 // Can't happen; the Package Manager is in this same process
1873             }
1874         }
1875
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();
1884                 } else {
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
1887                     // permission
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);
1892                 }
1893             }
1894             return canPersist;
1895         }
1896
1897         // IJobScheduler implementation
1898         @Override
1899         public int schedule(JobInfo job) throws RemoteException {
1900             if (DEBUG) {
1901                 Slog.d(TAG, "Scheduling job: " + job.toString());
1902             }
1903             final int pid = Binder.getCallingPid();
1904             final int uid = Binder.getCallingUid();
1905
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.");
1911                 }
1912             }
1913
1914             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1915                 getContext().enforceCallingOrSelfPermission(
1916                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1917             }
1918
1919             long ident = Binder.clearCallingIdentity();
1920             try {
1921                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1922             } finally {
1923                 Binder.restoreCallingIdentity(ident);
1924             }
1925         }
1926
1927         // IJobScheduler implementation
1928         @Override
1929         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1930             if (DEBUG) {
1931                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1932             }
1933             final int pid = Binder.getCallingPid();
1934             final int uid = Binder.getCallingUid();
1935
1936             enforceValidJobRequest(uid, job);
1937             if (job.isPersisted()) {
1938                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1939             }
1940             if (work == null) {
1941                 throw new NullPointerException("work is null");
1942             }
1943
1944             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1945                 getContext().enforceCallingOrSelfPermission(
1946                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1947             }
1948
1949             long ident = Binder.clearCallingIdentity();
1950             try {
1951                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
1952             } finally {
1953                 Binder.restoreCallingIdentity(ident);
1954             }
1955         }
1956
1957         @Override
1958         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1959                 throws RemoteException {
1960             final int callerUid = Binder.getCallingUid();
1961             if (DEBUG) {
1962                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1963                         + " on behalf of " + packageName);
1964             }
1965
1966             if (packageName == null) {
1967                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1968             }
1969
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");
1975             }
1976
1977             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1978                 getContext().enforceCallingOrSelfPermission(
1979                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1980             }
1981
1982             long ident = Binder.clearCallingIdentity();
1983             try {
1984                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
1985                         packageName, userId, tag);
1986             } finally {
1987                 Binder.restoreCallingIdentity(ident);
1988             }
1989         }
1990
1991         @Override
1992         public List<JobInfo> getAllPendingJobs() throws RemoteException {
1993             final int uid = Binder.getCallingUid();
1994
1995             long ident = Binder.clearCallingIdentity();
1996             try {
1997                 return JobSchedulerService.this.getPendingJobs(uid);
1998             } finally {
1999                 Binder.restoreCallingIdentity(ident);
2000             }
2001         }
2002
2003         @Override
2004         public JobInfo getPendingJob(int jobId) throws RemoteException {
2005             final int uid = Binder.getCallingUid();
2006
2007             long ident = Binder.clearCallingIdentity();
2008             try {
2009                 return JobSchedulerService.this.getPendingJob(uid, jobId);
2010             } finally {
2011                 Binder.restoreCallingIdentity(ident);
2012             }
2013         }
2014
2015         @Override
2016         public void cancelAll() throws RemoteException {
2017             final int uid = Binder.getCallingUid();
2018
2019             long ident = Binder.clearCallingIdentity();
2020             try {
2021                 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
2022             } finally {
2023                 Binder.restoreCallingIdentity(ident);
2024             }
2025         }
2026
2027         @Override
2028         public void cancel(int jobId) throws RemoteException {
2029             final int uid = Binder.getCallingUid();
2030
2031             long ident = Binder.clearCallingIdentity();
2032             try {
2033                 JobSchedulerService.this.cancelJob(uid, jobId);
2034             } finally {
2035                 Binder.restoreCallingIdentity(ident);
2036             }
2037         }
2038
2039         /**
2040          * "dumpsys" infrastructure
2041          */
2042         @Override
2043         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2044             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2045
2046             long identityToken = Binder.clearCallingIdentity();
2047             try {
2048                 JobSchedulerService.this.dumpInternal(pw, args);
2049             } finally {
2050                 Binder.restoreCallingIdentity(identityToken);
2051             }
2052         }
2053
2054         @Override
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);
2059         }
2060     };
2061
2062     // Shell command infrastructure: run the given job immediately
2063     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2064         if (DEBUG) {
2065             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2066                     + " " + jobId + " f=" + force);
2067         }
2068
2069         try {
2070             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2071                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2072             if (uid < 0) {
2073                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2074             }
2075
2076             synchronized (mLock) {
2077                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2078                 if (js == null) {
2079                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2080                 }
2081
2082                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2083                 if (!js.isConstraintsSatisfied()) {
2084                     js.overrideState = 0;
2085                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2086                 }
2087
2088                 queueReadyJobsForExecutionLocked();
2089                 maybeRunPendingJobsLocked();
2090             }
2091         } catch (RemoteException e) {
2092             // can't happen
2093         }
2094         return 0;
2095     }
2096
2097     // Shell command infrastructure: immediately timeout currently executing jobs
2098     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2099             boolean hasJobId, int jobId) {
2100         if (DEBUG) {
2101             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2102         }
2103
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)) {
2110                     foundSome = true;
2111                     pw.print("Timing out: ");
2112                     js.printUniqueId(pw);
2113                     pw.print(" ");
2114                     pw.println(js.getServiceComponent().flattenToShortString());
2115                 }
2116             }
2117             if (!foundSome) {
2118                 pw.println("No matching executing jobs found.");
2119             }
2120         }
2121         return 0;
2122     }
2123
2124     void setMonitorBattery(boolean enabled) {
2125         synchronized (mLock) {
2126             if (mBatteryController != null) {
2127                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2128             }
2129         }
2130     }
2131
2132     int getBatterySeq() {
2133         synchronized (mLock) {
2134             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2135         }
2136     }
2137
2138     boolean getBatteryCharging() {
2139         synchronized (mLock) {
2140             return mBatteryController != null
2141                     ? mBatteryController.getTracker().isOnStablePower() : false;
2142         }
2143     }
2144
2145     boolean getBatteryNotLow() {
2146         synchronized (mLock) {
2147             return mBatteryController != null
2148                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
2149         }
2150     }
2151
2152     int getStorageSeq() {
2153         synchronized (mLock) {
2154             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2155         }
2156     }
2157
2158     boolean getStorageNotLow() {
2159         synchronized (mLock) {
2160             return mStorageController != null
2161                     ? mStorageController.getTracker().isStorageNotLow() : false;
2162         }
2163     }
2164
2165     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2166         try {
2167             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2168                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2169             if (uid < 0) {
2170                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2171                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2172             }
2173
2174             synchronized (mLock) {
2175                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2176                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2177                 if (js == null) {
2178                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
2179                     pw.print("/jid"); pw.print(jobId); pw.println(")");
2180                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2181                 }
2182
2183                 boolean printed = false;
2184                 if (mPendingJobs.contains(js)) {
2185                     pw.print("pending");
2186                     printed = true;
2187                 }
2188                 if (isCurrentlyActiveLocked(js)) {
2189                     if (printed) {
2190                         pw.print(" ");
2191                     }
2192                     printed = true;
2193                     pw.println("active");
2194                 }
2195                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2196                     if (printed) {
2197                         pw.print(" ");
2198                     }
2199                     printed = true;
2200                     pw.println("user-stopped");
2201                 }
2202                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2203                     if (printed) {
2204                         pw.print(" ");
2205                     }
2206                     printed = true;
2207                     pw.println("backing-up");
2208                 }
2209                 boolean componentPresent = false;
2210                 try {
2211                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2212                             js.getServiceComponent(),
2213                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2214                             js.getUserId()) != null);
2215                 } catch (RemoteException e) {
2216                 }
2217                 if (!componentPresent) {
2218                     if (printed) {
2219                         pw.print(" ");
2220                     }
2221                     printed = true;
2222                     pw.println("no-component");
2223                 }
2224                 if (js.isReady()) {
2225                     if (printed) {
2226                         pw.print(" ");
2227                     }
2228                     printed = true;
2229                     pw.println("ready");
2230                 }
2231                 if (!printed) {
2232                     pw.print("waiting");
2233                 }
2234                 pw.println();
2235             }
2236         } catch (RemoteException e) {
2237             // can't happen
2238         }
2239         return 0;
2240     }
2241
2242     private String printContextIdToJobMap(JobStatus[] map, String initial) {
2243         StringBuilder s = new StringBuilder(initial + ": ");
2244         for (int i=0; i<map.length; i++) {
2245             s.append("(")
2246                     .append(map[i] == null? -1: map[i].getJobId())
2247                     .append(map[i] == null? -1: map[i].getUid())
2248                     .append(")" );
2249         }
2250         return s.toString();
2251     }
2252
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();
2258             s.append("(")
2259                     .append(js.getJob().getId())
2260                     .append(", ")
2261                     .append(js.getUid())
2262                     .append(") ");
2263         }
2264         return s.toString();
2265     }
2266
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.");
2272     }
2273
2274     void dumpInternal(final PrintWriter pw, String[] args) {
2275         int filterUid = -1;
2276         if (!ArrayUtils.isEmpty(args)) {
2277             int opti = 0;
2278             while (opti < args.length) {
2279                 String arg = args[opti];
2280                 if ("-h".equals(arg)) {
2281                     dumpHelp(pw);
2282                     return;
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);
2287                     return;
2288                 } else {
2289                     break;
2290                 }
2291                 opti++;
2292             }
2293             if (opti < args.length) {
2294                 String pkg = args[opti];
2295                 try {
2296                     filterUid = getContext().getPackageManager().getPackageUid(pkg,
2297                             PackageManager.MATCH_ANY_USER);
2298                 } catch (NameNotFoundException ignored) {
2299                     pw.println("Invalid package: " + pkg);
2300                     return;
2301                 }
2302             }
2303         }
2304
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);
2310             pw.println();
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>() {
2318                     @Override
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();
2324                         if (uid1 != uid2) {
2325                             return uid1 < uid2 ? -1 : 1;
2326                         }
2327                         return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
2328                     }
2329                 });
2330                 for (JobStatus job : jobs) {
2331                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
2332                     pw.println(job.toShortStringExceptUniqueId());
2333
2334                     // Skip printing details if the caller requested a filter
2335                     if (!job.shouldDump(filterUidFinal)) {
2336                         continue;
2337                     }
2338
2339                     job.dump(pw, "    ", true, nowElapsed);
2340                     pw.print("    Ready: ");
2341                     pw.print(isReadyToBeExecutedLocked(job));
2342                     pw.print(" (job=");
2343                     pw.print(job.isReady());
2344                     pw.print(" user=");
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));
2352                     pw.print(" comp=");
2353                     boolean componentPresent = false;
2354                     try {
2355                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2356                                 job.getServiceComponent(),
2357                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2358                                 job.getUserId()) != null);
2359                     } catch (RemoteException e) {
2360                     }
2361                     pw.print(componentPresent);
2362                     pw.println(")");
2363                 }
2364             } else {
2365                 pw.println("  None.");
2366             }
2367             for (int i=0; i<mControllers.size(); i++) {
2368                 pw.println();
2369                 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
2370             }
2371             pw.println();
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));
2378                 }
2379             }
2380             if (mBackingUpUids.size() > 0) {
2381                 pw.println();
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)) {
2387                         if (first) {
2388                             pw.print("  ");
2389                             first = false;
2390                         } else {
2391                             pw.print(", ");
2392                         }
2393                         pw.print(UserHandle.formatUid(uid));
2394                     }
2395                 }
2396                 pw.println();
2397             }
2398             pw.println();
2399             mJobPackageTracker.dump(pw, "", filterUidFinal);
2400             pw.println();
2401             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2402                 pw.println();
2403             }
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);
2413                 }
2414                 pw.print("    Tag: "); pw.println(job.getTag());
2415                 pw.print("    Enq: ");
2416                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
2417                 pw.println();
2418             }
2419             pw.println();
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();
2425                 if (job == null) {
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);
2431                     } else {
2432                         pw.println("inactive");
2433                     }
2434                     continue;
2435                 } else {
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);
2441                     pw.println();
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);
2446                     }
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);
2451                     pw.println();
2452                 }
2453             }
2454             if (filterUid == -1) {
2455                 pw.println();
2456                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2457                 pw.print("mReportedActive="); pw.println(mReportedActive);
2458                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2459             }
2460         }
2461         pw.println();
2462     }
2463 }