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
17package com.android.server.job;
18
19import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
22import java.io.FileDescriptor;
23import java.io.PrintWriter;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Collections;
27import java.util.Comparator;
28import java.util.Iterator;
29import java.util.List;
30
31import android.app.Activity;
32import android.app.ActivityManager;
33import android.app.AppGlobals;
34import android.app.IUidObserver;
35import android.app.job.JobInfo;
36import android.app.job.JobParameters;
37import android.app.job.JobScheduler;
38import android.app.job.JobService;
39import android.app.job.IJobScheduler;
40import android.app.job.JobWorkItem;
41import android.content.BroadcastReceiver;
42import android.content.ComponentName;
43import android.content.ContentResolver;
44import android.content.Context;
45import android.content.Intent;
46import android.content.IntentFilter;
47import android.content.pm.IPackageManager;
48import android.content.pm.PackageManager;
49import android.content.pm.ServiceInfo;
50import android.content.pm.PackageManager.NameNotFoundException;
51import android.database.ContentObserver;
52import android.net.Uri;
53import android.os.BatteryStats;
54import android.os.Binder;
55import android.os.Handler;
56import android.os.Looper;
57import android.os.Message;
58import android.os.Process;
59import android.os.PowerManager;
60import android.os.RemoteException;
61import android.os.ResultReceiver;
62import android.os.ServiceManager;
63import android.os.ShellCallback;
64import android.os.SystemClock;
65import android.os.UserHandle;
66import android.os.UserManagerInternal;
67import android.provider.Settings;
68import android.util.KeyValueListParser;
69import android.util.Slog;
70import android.util.SparseArray;
71import android.util.SparseIntArray;
72import android.util.TimeUtils;
73
74import com.android.internal.app.IBatteryStats;
75import com.android.internal.app.procstats.ProcessStats;
76import com.android.internal.util.ArrayUtils;
77import com.android.internal.util.DumpUtils;
78import com.android.server.DeviceIdleController;
79import com.android.server.LocalServices;
80import com.android.server.job.JobStore.JobStatusFunctor;
81import com.android.server.job.controllers.AppIdleController;
82import com.android.server.job.controllers.BatteryController;
83import com.android.server.job.controllers.ConnectivityController;
84import com.android.server.job.controllers.ContentObserverController;
85import com.android.server.job.controllers.DeviceIdleJobsController;
86import com.android.server.job.controllers.IdleController;
87import com.android.server.job.controllers.JobStatus;
88import com.android.server.job.controllers.StateController;
89import com.android.server.job.controllers.StorageController;
90import com.android.server.job.controllers.TimeController;
91
92import libcore.util.EmptyArray;
93
94/**
95 * Responsible for taking jobs representing work to be performed by a client app, and determining
96 * based on the criteria specified when that job should be run against the client application's
97 * endpoint.
98 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
99 * about constraints, or the state of active jobs. It receives callbacks from the various
100 * controllers and completed jobs and operates accordingly.
101 *
102 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
103 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
104 * @hide
105 */
106public final class JobSchedulerService extends com.android.server.SystemService
107        implements StateChangedListener, JobCompletedListener {
108    static final String TAG = "JobSchedulerService";
109    public static final boolean DEBUG = false;
110
111    /** The maximum number of concurrent jobs we run at one time. */
112    private static final int MAX_JOB_CONTEXTS_COUNT = 16;
113    /** Enforce a per-app limit on scheduled jobs? */
114    private static final boolean ENFORCE_MAX_JOBS = true;
115    /** The maximum number of jobs that we allow an unprivileged app to schedule */
116    private static final int MAX_JOBS_PER_APP = 100;
117
118
119    /** Global local for all job scheduler state. */
120    final Object mLock = new Object();
121    /** Master list of jobs. */
122    final JobStore mJobs;
123    /** Tracking amount of time each package runs for. */
124    final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
125
126    static final int MSG_JOB_EXPIRED = 0;
127    static final int MSG_CHECK_JOB = 1;
128    static final int MSG_STOP_JOB = 2;
129    static final int MSG_CHECK_JOB_GREEDY = 3;
130
131    /**
132     * Track Services that have currently active or pending jobs. The index is provided by
133     * {@link JobStatus#getServiceToken()}
134     */
135    final List<JobServiceContext> mActiveServices = new ArrayList<>();
136    /** List of controllers that will notify this service of updates to jobs. */
137    List<StateController> mControllers;
138    /** Need direct access to this for testing. */
139    BatteryController mBatteryController;
140    /** Need direct access to this for testing. */
141    StorageController mStorageController;
142    /**
143     * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
144     * when ready to execute them.
145     */
146    final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
147
148    int[] mStartedUsers = EmptyArray.INT;
149
150    final JobHandler mHandler;
151    final JobSchedulerStub mJobSchedulerStub;
152
153    IBatteryStats mBatteryStats;
154    PowerManager mPowerManager;
155    DeviceIdleController.LocalService mLocalDeviceIdleController;
156
157    /**
158     * Set to true once we are allowed to run third party apps.
159     */
160    boolean mReadyToRock;
161
162    /**
163     * What we last reported to DeviceIdleController about whether we are active.
164     */
165    boolean mReportedActive;
166
167    /**
168     * Current limit on the number of concurrent JobServiceContext entries we want to
169     * keep actively running a job.
170     */
171    int mMaxActiveJobs = 1;
172
173    /**
174     * Which uids are currently in the foreground.
175     */
176    final SparseIntArray mUidPriorityOverride = new SparseIntArray();
177
178    /**
179     * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
180     */
181    final SparseIntArray mBackingUpUids = new SparseIntArray();
182
183    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
184
185    /**
186     * This array essentially stores the state of mActiveServices array.
187     * The ith index stores the job present on the ith JobServiceContext.
188     * We manipulate this array until we arrive at what jobs should be running on
189     * what JobServiceContext.
190     */
191    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
192    /**
193     * Indicates whether we need to act on this jobContext id
194     */
195    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
196    /**
197     * The uid whose jobs we would like to assign to a context.
198     */
199    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
200
201    /**
202     * All times are in milliseconds. These constants are kept synchronized with the system
203     * global Settings. Any access to this class or its fields should be done while
204     * holding the JobSchedulerService.mLock lock.
205     */
206    private final class Constants extends ContentObserver {
207        // Key names stored in the settings value.
208        private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
209        private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
210        private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
211        private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
212        private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
213        private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
214        private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
215        private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
216        private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
217        private static final String KEY_FG_JOB_COUNT = "fg_job_count";
218        private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
219        private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
220        private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
221        private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
222        private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
223                = "max_standard_reschedule_count";
224        private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
225        private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
226        private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
227
228        private static final int DEFAULT_MIN_IDLE_COUNT = 1;
229        private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
230        private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
231        private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
232        private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
233        private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
234        private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
235        private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
236        private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
237        private static final int DEFAULT_FG_JOB_COUNT = 4;
238        private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
239        private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
240        private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
241        private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
242        private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
243        private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
244        private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
245        private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
246
247        /**
248         * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
249         * early.
250         */
251        int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
252        /**
253         * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
254         * things early.
255         */
256        int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
257        /**
258         * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
259         * schedule things early.
260         */
261        int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
262        /**
263         * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
264         * schedule things early.
265         */
266        int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
267        /**
268         * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
269         * things early.  1 == Run connectivity jobs as soon as ready.
270         */
271        int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
272        /**
273         * Minimum # of content trigger jobs that must be ready in order to force the JMS to
274         * schedule things early.
275         */
276        int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
277        /**
278         * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
279         * running some work early.  This (and thus the other min counts) is now set to 1, to
280         * prevent any batching at this level.  Since we now do batching through doze, that is
281         * a much better mechanism.
282         */
283        int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
284        /**
285         * This is the job execution factor that is considered to be heavy use of the system.
286         */
287        float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
288        /**
289         * This is the job execution factor that is considered to be moderate use of the system.
290         */
291        float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
292        /**
293         * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
294         */
295        int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
296        /**
297         * The maximum number of background jobs we allow when the system is in a normal
298         * memory state.
299         */
300        int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
301        /**
302         * The maximum number of background jobs we allow when the system is in a moderate
303         * memory state.
304         */
305        int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
306        /**
307         * The maximum number of background jobs we allow when the system is in a low
308         * memory state.
309         */
310        int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
311        /**
312         * The maximum number of background jobs we allow when the system is in a critical
313         * memory state.
314         */
315        int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
316        /**
317         * The maximum number of times we allow a job to have itself rescheduled before
318         * giving up on it, for standard jobs.
319         */
320        int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
321        /**
322         * The maximum number of times we allow a job to have itself rescheduled before
323         * giving up on it, for jobs that are executing work.
324         */
325        int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
326        /**
327         * The minimum backoff time to allow for linear backoff.
328         */
329        long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
330        /**
331         * The minimum backoff time to allow for exponential backoff.
332         */
333        long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
334
335        private ContentResolver mResolver;
336        private final KeyValueListParser mParser = new KeyValueListParser(',');
337
338        public Constants(Handler handler) {
339            super(handler);
340        }
341
342        public void start(ContentResolver resolver) {
343            mResolver = resolver;
344            mResolver.registerContentObserver(Settings.Global.getUriFor(
345                    Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
346            updateConstants();
347        }
348
349        @Override
350        public void onChange(boolean selfChange, Uri uri) {
351            updateConstants();
352        }
353
354        private void updateConstants() {
355            synchronized (mLock) {
356                try {
357                    mParser.setString(Settings.Global.getString(mResolver,
358                            Settings.Global.ALARM_MANAGER_CONSTANTS));
359                } catch (IllegalArgumentException e) {
360                    // Failed to parse the settings string, log this and move on
361                    // with defaults.
362                    Slog.e(TAG, "Bad device idle settings", e);
363                }
364
365                MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
366                        DEFAULT_MIN_IDLE_COUNT);
367                MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
368                        DEFAULT_MIN_CHARGING_COUNT);
369                MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
370                        DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
371                MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
372                        DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
373                MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
374                        DEFAULT_MIN_CONNECTIVITY_COUNT);
375                MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
376                        DEFAULT_MIN_CONTENT_COUNT);
377                MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
378                        DEFAULT_MIN_READY_JOBS_COUNT);
379                HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
380                        DEFAULT_HEAVY_USE_FACTOR);
381                MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
382                        DEFAULT_MODERATE_USE_FACTOR);
383                FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
384                        DEFAULT_FG_JOB_COUNT);
385                BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
386                        DEFAULT_BG_NORMAL_JOB_COUNT);
387                if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
388                    BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
389                }
390                BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
391                        DEFAULT_BG_MODERATE_JOB_COUNT);
392                if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
393                    BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
394                }
395                BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
396                        DEFAULT_BG_LOW_JOB_COUNT);
397                if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
398                    BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
399                }
400                BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
401                        DEFAULT_BG_CRITICAL_JOB_COUNT);
402                if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
403                    BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
404                }
405                MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
406                        DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
407                MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
408                        DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
409                MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
410                        DEFAULT_MIN_LINEAR_BACKOFF_TIME);
411                MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
412                        DEFAULT_MIN_EXP_BACKOFF_TIME);
413            }
414        }
415
416        void dump(PrintWriter pw) {
417            pw.println("  Settings:");
418
419            pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
420            pw.print(MIN_IDLE_COUNT); pw.println();
421
422            pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
423            pw.print(MIN_CHARGING_COUNT); pw.println();
424
425            pw.print("    "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
426            pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
427
428            pw.print("    "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
429            pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
430
431            pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
432            pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
433
434            pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
435            pw.print(MIN_CONTENT_COUNT); pw.println();
436
437            pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
438            pw.print(MIN_READY_JOBS_COUNT); pw.println();
439
440            pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
441            pw.print(HEAVY_USE_FACTOR); pw.println();
442
443            pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
444            pw.print(MODERATE_USE_FACTOR); pw.println();
445
446            pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
447            pw.print(FG_JOB_COUNT); pw.println();
448
449            pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
450            pw.print(BG_NORMAL_JOB_COUNT); pw.println();
451
452            pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
453            pw.print(BG_MODERATE_JOB_COUNT); pw.println();
454
455            pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
456            pw.print(BG_LOW_JOB_COUNT); pw.println();
457
458            pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
459            pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
460
461            pw.print("    "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
462            pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
463
464            pw.print("    "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
465            pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
466
467            pw.print("    "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
468            pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
469
470            pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
471            pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
472        }
473    }
474
475    final Constants mConstants;
476
477    static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
478        if (o1.enqueueTime < o2.enqueueTime) {
479            return -1;
480        }
481        return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
482    };
483
484    static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
485        int where = Collections.binarySearch(array, newItem, comparator);
486        if (where < 0) {
487            where = ~where;
488        }
489        array.add(where, newItem);
490    }
491
492    /**
493     * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
494     * still clean up. On reinstall the package will have a new uid.
495     */
496    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
497        @Override
498        public void onReceive(Context context, Intent intent) {
499            final String action = intent.getAction();
500            if (DEBUG) {
501                Slog.d(TAG, "Receieved: " + action);
502            }
503            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
504                // Purge the app's jobs if the whole package was just disabled.  When this is
505                // the case the component name will be a bare package name.
506                final String pkgName = getPackageName(intent);
507                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
508                if (pkgName != null && pkgUid != -1) {
509                    final String[] changedComponents = intent.getStringArrayExtra(
510                            Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
511                    if (changedComponents != null) {
512                        for (String component : changedComponents) {
513                            if (component.equals(pkgName)) {
514                                if (DEBUG) {
515                                    Slog.d(TAG, "Package state change: " + pkgName);
516                                }
517                                try {
518                                    final int userId = UserHandle.getUserId(pkgUid);
519                                    IPackageManager pm = AppGlobals.getPackageManager();
520                                    final int state = pm.getApplicationEnabledSetting(pkgName, userId);
521                                    if (state == COMPONENT_ENABLED_STATE_DISABLED
522                                            || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
523                                        if (DEBUG) {
524                                            Slog.d(TAG, "Removing jobs for package " + pkgName
525                                                    + " in user " + userId);
526                                        }
527                                        cancelJobsForUid(pkgUid, "app package state changed");
528                                    }
529                                } catch (RemoteException|IllegalArgumentException e) {
530                                    /*
531                                     * IllegalArgumentException means that the package doesn't exist.
532                                     * This arises when PACKAGE_CHANGED broadcast delivery has lagged
533                                     * behind outright uninstall, so by the time we try to act it's gone.
534                                     * We don't need to act on this PACKAGE_CHANGED when this happens;
535                                     * we'll get a PACKAGE_REMOVED later and clean up then.
536                                     *
537                                     * RemoteException can't actually happen; the package manager is
538                                     * running in this same process.
539                                     */
540                                }
541                                break;
542                            }
543                        }
544                    }
545                } else {
546                    Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
547                }
548            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
549                // If this is an outright uninstall rather than the first half of an
550                // app update sequence, cancel the jobs associated with the app.
551                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
552                    int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
553                    if (DEBUG) {
554                        Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
555                    }
556                    cancelJobsForUid(uidRemoved, "app uninstalled");
557                }
558            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
559                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
560                if (DEBUG) {
561                    Slog.d(TAG, "Removing jobs for user: " + userId);
562                }
563                cancelJobsForUser(userId);
564            } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
565                // Has this package scheduled any jobs, such that we will take action
566                // if it were to be force-stopped?
567                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
568                final String pkgName = intent.getData().getSchemeSpecificPart();
569                if (pkgUid != -1) {
570                    List<JobStatus> jobsForUid;
571                    synchronized (mLock) {
572                        jobsForUid = mJobs.getJobsByUid(pkgUid);
573                    }
574                    for (int i = jobsForUid.size() - 1; i >= 0; i--) {
575                        if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
576                            if (DEBUG) {
577                                Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
578                                        + pkgUid + " has jobs");
579                            }
580                            setResultCode(Activity.RESULT_OK);
581                            break;
582                        }
583                    }
584                }
585            } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
586                // possible force-stop
587                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
588                final String pkgName = intent.getData().getSchemeSpecificPart();
589                if (pkgUid != -1) {
590                    if (DEBUG) {
591                        Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
592                    }
593                    cancelJobsForPackageAndUid(pkgName, pkgUid);
594                }
595            }
596        }
597    };
598
599    private String getPackageName(Intent intent) {
600        Uri uri = intent.getData();
601        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
602        return pkg;
603    }
604
605    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
606        @Override public void onUidStateChanged(int uid, int procState,
607                long procStateSeq) throws RemoteException {
608            updateUidState(uid, procState);
609        }
610
611        @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
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) throws RemoteException {
622            if (disabled) {
623                cancelJobsForUid(uid, "app uid idle");
624            }
625        }
626    };
627
628    public Object getLock() {
629        return mLock;
630    }
631
632    public JobStore getJobStore() {
633        return mJobs;
634    }
635
636    @Override
637    public void onStartUser(int userHandle) {
638        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
639        // Let's kick any outstanding jobs for this user.
640        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
641    }
642
643    @Override
644    public void onUnlockUser(int userHandle) {
645        // Let's kick any outstanding jobs for this user.
646        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
647    }
648
649    @Override
650    public void onStopUser(int userHandle) {
651        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
652    }
653
654    public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
655            int userId, String tag) {
656        try {
657            if (ActivityManager.getService().isAppStartModeDisabled(uId,
658                    job.getService().getPackageName())) {
659                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
660                        + " -- package not allowed to start");
661                return JobScheduler.RESULT_FAILURE;
662            }
663        } catch (RemoteException e) {
664        }
665        synchronized (mLock) {
666            final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
667
668            if (work != null && toCancel != null) {
669                // Fast path: we are adding work to an existing job, and the JobInfo is not
670                // changing.  We can just directly enqueue this work in to the job.
671                if (toCancel.getJob().equals(job)) {
672                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
673                    return JobScheduler.RESULT_SUCCESS;
674                }
675            }
676
677            JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
678            if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
679            // Jobs on behalf of others don't apply to the per-app job cap
680            if (ENFORCE_MAX_JOBS && packageName == null) {
681                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
682                    Slog.w(TAG, "Too many jobs for uid " + uId);
683                    throw new IllegalStateException("Apps may not schedule more than "
684                                + MAX_JOBS_PER_APP + " distinct jobs");
685                }
686            }
687
688            // This may throw a SecurityException.
689            jobStatus.prepareLocked(ActivityManager.getService());
690
691            if (toCancel != null) {
692                cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
693            }
694            if (work != null) {
695                // If work has been supplied, enqueue it into the new job.
696                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
697            }
698            startTrackingJobLocked(jobStatus, toCancel);
699
700            // If the job is immediately ready to run, then we can just immediately
701            // put it in the pending list and try to schedule it.  This is especially
702            // important for jobs with a 0 deadline constraint, since they will happen a fair
703            // amount, we want to handle them as quickly as possible, and semantically we want to
704            // make sure we have started holding the wake lock for the job before returning to
705            // the caller.
706            // If the job is not yet ready to run, there is nothing more to do -- we are
707            // now just waiting for one of its controllers to change state and schedule
708            // the job appropriately.
709            if (isReadyToBeExecutedLocked(jobStatus)) {
710                // This is a new job, we can just immediately put it on the pending
711                // list and try to run it.
712                mJobPackageTracker.notePending(jobStatus);
713                addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
714                maybeRunPendingJobsLocked();
715            }
716        }
717        return JobScheduler.RESULT_SUCCESS;
718    }
719
720    public List<JobInfo> getPendingJobs(int uid) {
721        synchronized (mLock) {
722            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
723            ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
724            for (int i = jobs.size() - 1; i >= 0; i--) {
725                JobStatus job = jobs.get(i);
726                outList.add(job.getJob());
727            }
728            return outList;
729        }
730    }
731
732    public JobInfo getPendingJob(int uid, int jobId) {
733        synchronized (mLock) {
734            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
735            for (int i = jobs.size() - 1; i >= 0; i--) {
736                JobStatus job = jobs.get(i);
737                if (job.getJobId() == jobId) {
738                    return job.getJob();
739                }
740            }
741            return null;
742        }
743    }
744
745    void cancelJobsForUser(int userHandle) {
746        synchronized (mLock) {
747            final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
748            for (int i=0; i<jobsForUser.size(); i++) {
749                JobStatus toRemove = jobsForUser.get(i);
750                cancelJobImplLocked(toRemove, null, "user removed");
751            }
752        }
753    }
754
755    private void cancelJobsForNonExistentUsers() {
756        UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
757        synchronized (mLock) {
758            mJobs.removeJobsOfNonUsers(umi.getUserIds());
759        }
760    }
761
762    void cancelJobsForPackageAndUid(String pkgName, int uid) {
763        synchronized (mLock) {
764            final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
765            for (int i = jobsForUid.size() - 1; i >= 0; i--) {
766                final JobStatus job = jobsForUid.get(i);
767                if (job.getSourcePackageName().equals(pkgName)) {
768                    cancelJobImplLocked(job, null, "app force stopped");
769                }
770            }
771        }
772    }
773
774    /**
775     * Entry point from client to cancel all jobs originating from their uid.
776     * This will remove the job from the master list, and cancel the job if it was staged for
777     * execution or being executed.
778     * @param uid Uid to check against for removal of a job.
779     *
780     */
781    public void cancelJobsForUid(int uid, String reason) {
782        synchronized (mLock) {
783            final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
784            for (int i=0; i<jobsForUid.size(); i++) {
785                JobStatus toRemove = jobsForUid.get(i);
786                cancelJobImplLocked(toRemove, null, reason);
787            }
788        }
789    }
790
791    /**
792     * Entry point from client to cancel the job corresponding to the jobId provided.
793     * This will remove the job from the master list, and cancel the job if it was staged for
794     * execution or being executed.
795     * @param uid Uid of the calling client.
796     * @param jobId Id of the job, provided at schedule-time.
797     */
798    public void cancelJob(int uid, int jobId) {
799        JobStatus toCancel;
800        synchronized (mLock) {
801            toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
802            if (toCancel != null) {
803                cancelJobImplLocked(toCancel, null, "cancel() called by app");
804            }
805        }
806    }
807
808    private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
809        if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
810        cancelled.unprepareLocked(ActivityManager.getService());
811        stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
812        // Remove from pending queue.
813        if (mPendingJobs.remove(cancelled)) {
814            mJobPackageTracker.noteNonpending(cancelled);
815        }
816        // Cancel if running.
817        stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
818        reportActiveLocked();
819    }
820
821    void updateUidState(int uid, int procState) {
822        synchronized (mLock) {
823            if (procState == ActivityManager.PROCESS_STATE_TOP) {
824                // Only use this if we are exactly the top app.  All others can live
825                // with just the foreground priority.  This means that persistent processes
826                // can never be the top app priority...  that is fine.
827                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
828            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
829                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
830            } else {
831                mUidPriorityOverride.delete(uid);
832            }
833        }
834    }
835
836    @Override
837    public void onDeviceIdleStateChanged(boolean deviceIdle) {
838        synchronized (mLock) {
839            if (deviceIdle) {
840                // When becoming idle, make sure no jobs are actively running,
841                // except those using the idle exemption flag.
842                for (int i=0; i<mActiveServices.size(); i++) {
843                    JobServiceContext jsc = mActiveServices.get(i);
844                    final JobStatus executing = jsc.getRunningJobLocked();
845                    if (executing != null
846                            && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
847                        jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
848                                "cancelled due to doze");
849                    }
850                }
851            } else {
852                // When coming out of idle, allow thing to start back up.
853                if (mReadyToRock) {
854                    if (mLocalDeviceIdleController != null) {
855                        if (!mReportedActive) {
856                            mReportedActive = true;
857                            mLocalDeviceIdleController.setJobsActive(true);
858                        }
859                    }
860                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
861                }
862            }
863        }
864    }
865
866    void reportActiveLocked() {
867        // active is true if pending queue contains jobs OR some job is running.
868        boolean active = mPendingJobs.size() > 0;
869        if (mPendingJobs.size() <= 0) {
870            for (int i=0; i<mActiveServices.size(); i++) {
871                final JobServiceContext jsc = mActiveServices.get(i);
872                final JobStatus job = jsc.getRunningJobLocked();
873                if (job != null
874                        && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
875                        && !job.dozeWhitelisted) {
876                    // We will report active if we have a job running and it is not an exception
877                    // due to being in the foreground or whitelisted.
878                    active = true;
879                    break;
880                }
881            }
882        }
883
884        if (mReportedActive != active) {
885            mReportedActive = active;
886            if (mLocalDeviceIdleController != null) {
887                mLocalDeviceIdleController.setJobsActive(active);
888            }
889        }
890    }
891
892    /**
893     * Initializes the system service.
894     * <p>
895     * Subclasses must define a single argument constructor that accepts the context
896     * and passes it to super.
897     * </p>
898     *
899     * @param context The system server context.
900     */
901    public JobSchedulerService(Context context) {
902        super(context);
903        mHandler = new JobHandler(context.getMainLooper());
904        mConstants = new Constants(mHandler);
905        mJobSchedulerStub = new JobSchedulerStub();
906        mJobs = JobStore.initAndGet(this);
907
908        // Create the controllers.
909        mControllers = new ArrayList<StateController>();
910        mControllers.add(ConnectivityController.get(this));
911        mControllers.add(TimeController.get(this));
912        mControllers.add(IdleController.get(this));
913        mBatteryController = BatteryController.get(this);
914        mControllers.add(mBatteryController);
915        mStorageController = StorageController.get(this);
916        mControllers.add(mStorageController);
917        mControllers.add(AppIdleController.get(this));
918        mControllers.add(ContentObserverController.get(this));
919        mControllers.add(DeviceIdleJobsController.get(this));
920    }
921
922    @Override
923    public void onStart() {
924        publishLocalService(JobSchedulerInternal.class, new LocalService());
925        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
926    }
927
928    @Override
929    public void onBootPhase(int phase) {
930        if (PHASE_SYSTEM_SERVICES_READY == phase) {
931            mConstants.start(getContext().getContentResolver());
932            // Register br for package removals and user removals.
933            final IntentFilter filter = new IntentFilter();
934            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
935            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
936            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
937            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
938            filter.addDataScheme("package");
939            getContext().registerReceiverAsUser(
940                    mBroadcastReceiver, UserHandle.ALL, filter, null, null);
941            final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
942            getContext().registerReceiverAsUser(
943                    mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
944            mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
945            try {
946                ActivityManager.getService().registerUidObserver(mUidObserver,
947                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
948                        | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
949                        null);
950            } catch (RemoteException e) {
951                // ignored; both services live in system_server
952            }
953            // Remove any jobs that are not associated with any of the current users.
954            cancelJobsForNonExistentUsers();
955        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
956            synchronized (mLock) {
957                // Let's go!
958                mReadyToRock = true;
959                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
960                        BatteryStats.SERVICE_NAME));
961                mLocalDeviceIdleController
962                        = LocalServices.getService(DeviceIdleController.LocalService.class);
963                // Create the "runners".
964                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
965                    mActiveServices.add(
966                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
967                                    getContext().getMainLooper()));
968                }
969                // Attach jobs to their controllers.
970                mJobs.forEachJob(new JobStatusFunctor() {
971                    @Override
972                    public void process(JobStatus job) {
973                        for (int controller = 0; controller < mControllers.size(); controller++) {
974                            final StateController sc = mControllers.get(controller);
975                            sc.maybeStartTrackingJobLocked(job, null);
976                        }
977                    }
978                });
979                // GO GO GO!
980                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
981            }
982        }
983    }
984
985    /**
986     * Called when we have a job status object that we need to insert in our
987     * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
988     * about.
989     */
990    private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
991        if (!jobStatus.isPreparedLocked()) {
992            Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
993        }
994        jobStatus.enqueueTime = SystemClock.elapsedRealtime();
995        final boolean update = mJobs.add(jobStatus);
996        if (mReadyToRock) {
997            for (int i = 0; i < mControllers.size(); i++) {
998                StateController controller = mControllers.get(i);
999                if (update) {
1000                    controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1001                }
1002                controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1003            }
1004        }
1005    }
1006
1007    /**
1008     * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1009     * object removed.
1010     */
1011    private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1012            boolean writeBack) {
1013        // Deal with any remaining work items in the old job.
1014        jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
1015
1016        // Remove from store as well as controllers.
1017        final boolean removed = mJobs.remove(jobStatus, writeBack);
1018        if (removed && mReadyToRock) {
1019            for (int i=0; i<mControllers.size(); i++) {
1020                StateController controller = mControllers.get(i);
1021                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1022            }
1023        }
1024        return removed;
1025    }
1026
1027    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
1028        for (int i=0; i<mActiveServices.size(); i++) {
1029            JobServiceContext jsc = mActiveServices.get(i);
1030            final JobStatus executing = jsc.getRunningJobLocked();
1031            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1032                jsc.cancelExecutingJobLocked(reason, debugReason);
1033                return true;
1034            }
1035        }
1036        return false;
1037    }
1038
1039    /**
1040     * @param job JobStatus we are querying against.
1041     * @return Whether or not the job represented by the status object is currently being run or
1042     * is pending.
1043     */
1044    private boolean isCurrentlyActiveLocked(JobStatus job) {
1045        for (int i=0; i<mActiveServices.size(); i++) {
1046            JobServiceContext serviceContext = mActiveServices.get(i);
1047            final JobStatus running = serviceContext.getRunningJobLocked();
1048            if (running != null && running.matches(job.getUid(), job.getJobId())) {
1049                return true;
1050            }
1051        }
1052        return false;
1053    }
1054
1055    void noteJobsPending(List<JobStatus> jobs) {
1056        for (int i = jobs.size() - 1; i >= 0; i--) {
1057            JobStatus job = jobs.get(i);
1058            mJobPackageTracker.notePending(job);
1059        }
1060    }
1061
1062    void noteJobsNonpending(List<JobStatus> jobs) {
1063        for (int i = jobs.size() - 1; i >= 0; i--) {
1064            JobStatus job = jobs.get(i);
1065            mJobPackageTracker.noteNonpending(job);
1066        }
1067    }
1068
1069    /**
1070     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1071     * specify an override deadline on a failed job (the failed job will run even though it's not
1072     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1073     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1074     *
1075     * @param failureToReschedule Provided job status that we will reschedule.
1076     * @return A newly instantiated JobStatus with the same constraints as the last job except
1077     * with adjusted timing constraints.
1078     *
1079     * @see #maybeQueueReadyJobsForExecutionLocked
1080     */
1081    private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1082        final long elapsedNowMillis = SystemClock.elapsedRealtime();
1083        final JobInfo job = failureToReschedule.getJob();
1084
1085        final long initialBackoffMillis = job.getInitialBackoffMillis();
1086        final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1087        long delayMillis;
1088
1089        if (failureToReschedule.hasWorkLocked()) {
1090            if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1091                Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1092                        + backoffAttempts + " > work limit "
1093                        + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1094                return null;
1095            }
1096        } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1097            Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1098                    + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1099            return null;
1100        }
1101
1102        switch (job.getBackoffPolicy()) {
1103            case JobInfo.BACKOFF_POLICY_LINEAR: {
1104                long backoff = initialBackoffMillis;
1105                if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1106                    backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1107                }
1108                delayMillis = backoff * backoffAttempts;
1109            } break;
1110            default:
1111                if (DEBUG) {
1112                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1113                }
1114            case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1115                long backoff = initialBackoffMillis;
1116                if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1117                    backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1118                }
1119                delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1120            } break;
1121        }
1122        delayMillis =
1123                Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1124        JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
1125                JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
1126        for (int ic=0; ic<mControllers.size(); ic++) {
1127            StateController controller = mControllers.get(ic);
1128            controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1129        }
1130        return newJob;
1131    }
1132
1133    /**
1134     * Called after a periodic has executed so we can reschedule it. We take the last execution
1135     * time of the job to be the time of completion (i.e. the time at which this function is
1136     * called).
1137     * This could be inaccurate b/c the job can run for as long as
1138     * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1139     * to underscheduling at least, rather than if we had taken the last execution time to be the
1140     * start of the execution.
1141     * @return A new job representing the execution criteria for this instantiation of the
1142     * recurring job.
1143     */
1144    private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1145        final long elapsedNow = SystemClock.elapsedRealtime();
1146        // Compute how much of the period is remaining.
1147        long runEarly = 0L;
1148
1149        // If this periodic was rescheduled it won't have a deadline.
1150        if (periodicToReschedule.hasDeadlineConstraint()) {
1151            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1152        }
1153        long flex = periodicToReschedule.getJob().getFlexMillis();
1154        long period = periodicToReschedule.getJob().getIntervalMillis();
1155        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1156        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1157
1158        if (DEBUG) {
1159            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1160                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1161        }
1162        return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1163                newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1164    }
1165
1166    // JobCompletedListener implementations.
1167
1168    /**
1169     * A job just finished executing. We fetch the
1170     * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1171     * whether we want to reschedule we readd it to the controllers.
1172     * @param jobStatus Completed job.
1173     * @param needsReschedule Whether the implementing class should reschedule this job.
1174     */
1175    @Override
1176    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1177        if (DEBUG) {
1178            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1179        }
1180
1181        // If the job wants to be rescheduled, we first need to make the next upcoming
1182        // job so we can transfer any appropriate state over from the previous job when
1183        // we stop it.
1184        final JobStatus rescheduledJob = needsReschedule
1185                ? getRescheduleJobForFailureLocked(jobStatus) : null;
1186
1187        // Do not write back immediately if this is a periodic job. The job may get lost if system
1188        // shuts down before it is added back.
1189        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1190            if (DEBUG) {
1191                Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1192            }
1193            // We still want to check for jobs to execute, because this job may have
1194            // scheduled a new job under the same job id, and now we can run it.
1195            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1196            return;
1197        }
1198
1199        if (rescheduledJob != null) {
1200            try {
1201                rescheduledJob.prepareLocked(ActivityManager.getService());
1202            } catch (SecurityException e) {
1203                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1204            }
1205            startTrackingJobLocked(rescheduledJob, jobStatus);
1206        } else if (jobStatus.getJob().isPeriodic()) {
1207            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1208            try {
1209                rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1210            } catch (SecurityException e) {
1211                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1212            }
1213            startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1214        }
1215        jobStatus.unprepareLocked(ActivityManager.getService());
1216        reportActiveLocked();
1217        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1218    }
1219
1220    // StateChangedListener implementations.
1221
1222    /**
1223     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1224     * some controller's state has changed, so as to run through the list of jobs and start/stop
1225     * any that are eligible.
1226     */
1227    @Override
1228    public void onControllerStateChanged() {
1229        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1230    }
1231
1232    @Override
1233    public void onRunJobNow(JobStatus jobStatus) {
1234        mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1235    }
1236
1237    final private class JobHandler extends Handler {
1238
1239        public JobHandler(Looper looper) {
1240            super(looper);
1241        }
1242
1243        @Override
1244        public void handleMessage(Message message) {
1245            synchronized (mLock) {
1246                if (!mReadyToRock) {
1247                    return;
1248                }
1249                switch (message.what) {
1250                    case MSG_JOB_EXPIRED: {
1251                        JobStatus runNow = (JobStatus) message.obj;
1252                        // runNow can be null, which is a controller's way of indicating that its
1253                        // state is such that all ready jobs should be run immediately.
1254                        if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
1255                            mJobPackageTracker.notePending(runNow);
1256                            addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
1257                        } else {
1258                            queueReadyJobsForExecutionLocked();
1259                        }
1260                    } break;
1261                    case MSG_CHECK_JOB:
1262                        if (mReportedActive) {
1263                            // if jobs are currently being run, queue all ready jobs for execution.
1264                            queueReadyJobsForExecutionLocked();
1265                        } else {
1266                            // Check the list of jobs and run some of them if we feel inclined.
1267                            maybeQueueReadyJobsForExecutionLocked();
1268                        }
1269                        break;
1270                    case MSG_CHECK_JOB_GREEDY:
1271                        queueReadyJobsForExecutionLocked();
1272                        break;
1273                    case MSG_STOP_JOB:
1274                        cancelJobImplLocked((JobStatus) message.obj, null,
1275                                "app no longer allowed to run");
1276                        break;
1277                }
1278                maybeRunPendingJobsLocked();
1279                // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1280                removeMessages(MSG_CHECK_JOB);
1281            }
1282        }
1283    }
1284
1285    private void stopNonReadyActiveJobsLocked() {
1286        for (int i=0; i<mActiveServices.size(); i++) {
1287            JobServiceContext serviceContext = mActiveServices.get(i);
1288            final JobStatus running = serviceContext.getRunningJobLocked();
1289            if (running != null && !running.isReady()) {
1290                serviceContext.cancelExecutingJobLocked(
1291                        JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1292                        "cancelled due to unsatisfied constraints");
1293            }
1294        }
1295    }
1296
1297    /**
1298     * Run through list of jobs and execute all possible - at least one is expired so we do
1299     * as many as we can.
1300     */
1301    private void queueReadyJobsForExecutionLocked() {
1302        if (DEBUG) {
1303            Slog.d(TAG, "queuing all ready jobs for execution:");
1304        }
1305        noteJobsNonpending(mPendingJobs);
1306        mPendingJobs.clear();
1307        stopNonReadyActiveJobsLocked();
1308        mJobs.forEachJob(mReadyQueueFunctor);
1309        mReadyQueueFunctor.postProcess();
1310
1311        if (DEBUG) {
1312            final int queuedJobs = mPendingJobs.size();
1313            if (queuedJobs == 0) {
1314                Slog.d(TAG, "No jobs pending.");
1315            } else {
1316                Slog.d(TAG, queuedJobs + " jobs queued.");
1317            }
1318        }
1319    }
1320
1321    final class ReadyJobQueueFunctor implements JobStatusFunctor {
1322        ArrayList<JobStatus> newReadyJobs;
1323
1324        @Override
1325        public void process(JobStatus job) {
1326            if (isReadyToBeExecutedLocked(job)) {
1327                if (DEBUG) {
1328                    Slog.d(TAG, "    queued " + job.toShortString());
1329                }
1330                if (newReadyJobs == null) {
1331                    newReadyJobs = new ArrayList<JobStatus>();
1332                }
1333                newReadyJobs.add(job);
1334            }
1335        }
1336
1337        public void postProcess() {
1338            if (newReadyJobs != null) {
1339                noteJobsPending(newReadyJobs);
1340                mPendingJobs.addAll(newReadyJobs);
1341                if (mPendingJobs.size() > 1) {
1342                    mPendingJobs.sort(mEnqueueTimeComparator);
1343                }
1344            }
1345            newReadyJobs = null;
1346        }
1347    }
1348    private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1349
1350    /**
1351     * The state of at least one job has changed. Here is where we could enforce various
1352     * policies on when we want to execute jobs.
1353     * Right now the policy is such:
1354     * If >1 of the ready jobs is idle mode we send all of them off
1355     * if more than 2 network connectivity jobs are ready we send them all off.
1356     * If more than 4 jobs total are ready we send them all off.
1357     * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1358     */
1359    final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1360        int chargingCount;
1361        int batteryNotLowCount;
1362        int storageNotLowCount;
1363        int idleCount;
1364        int backoffCount;
1365        int connectivityCount;
1366        int contentCount;
1367        List<JobStatus> runnableJobs;
1368
1369        public MaybeReadyJobQueueFunctor() {
1370            reset();
1371        }
1372
1373        // Functor method invoked for each job via JobStore.forEachJob()
1374        @Override
1375        public void process(JobStatus job) {
1376            if (isReadyToBeExecutedLocked(job)) {
1377                try {
1378                    if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1379                            job.getJob().getService().getPackageName())) {
1380                        Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1381                                + job.getJob().toString() + " -- package not allowed to start");
1382                        mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1383                        return;
1384                    }
1385                } catch (RemoteException e) {
1386                }
1387                if (job.getNumFailures() > 0) {
1388                    backoffCount++;
1389                }
1390                if (job.hasIdleConstraint()) {
1391                    idleCount++;
1392                }
1393                if (job.hasConnectivityConstraint()) {
1394                    connectivityCount++;
1395                }
1396                if (job.hasChargingConstraint()) {
1397                    chargingCount++;
1398                }
1399                if (job.hasBatteryNotLowConstraint()) {
1400                    batteryNotLowCount++;
1401                }
1402                if (job.hasStorageNotLowConstraint()) {
1403                    storageNotLowCount++;
1404                }
1405                if (job.hasContentTriggerConstraint()) {
1406                    contentCount++;
1407                }
1408                if (runnableJobs == null) {
1409                    runnableJobs = new ArrayList<>();
1410                }
1411                runnableJobs.add(job);
1412            }
1413        }
1414
1415        public void postProcess() {
1416            if (backoffCount > 0 ||
1417                    idleCount >= mConstants.MIN_IDLE_COUNT ||
1418                    connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1419                    chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1420                    batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1421                    storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1422                    contentCount >= mConstants.MIN_CONTENT_COUNT ||
1423                    (runnableJobs != null
1424                            && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1425                if (DEBUG) {
1426                    Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1427                }
1428                noteJobsPending(runnableJobs);
1429                mPendingJobs.addAll(runnableJobs);
1430                if (mPendingJobs.size() > 1) {
1431                    mPendingJobs.sort(mEnqueueTimeComparator);
1432                }
1433            } else {
1434                if (DEBUG) {
1435                    Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1436                }
1437            }
1438
1439            // Be ready for next time
1440            reset();
1441        }
1442
1443        private void reset() {
1444            chargingCount = 0;
1445            idleCount =  0;
1446            backoffCount = 0;
1447            connectivityCount = 0;
1448            batteryNotLowCount = 0;
1449            storageNotLowCount = 0;
1450            contentCount = 0;
1451            runnableJobs = null;
1452        }
1453    }
1454    private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1455
1456    private void maybeQueueReadyJobsForExecutionLocked() {
1457        if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1458
1459        noteJobsNonpending(mPendingJobs);
1460        mPendingJobs.clear();
1461        stopNonReadyActiveJobsLocked();
1462        mJobs.forEachJob(mMaybeQueueFunctor);
1463        mMaybeQueueFunctor.postProcess();
1464    }
1465
1466    /**
1467     * Criteria for moving a job into the pending queue:
1468     *      - It's ready.
1469     *      - It's not pending.
1470     *      - It's not already running on a JSC.
1471     *      - The user that requested the job is running.
1472     *      - The component is enabled and runnable.
1473     */
1474    private boolean isReadyToBeExecutedLocked(JobStatus job) {
1475        final boolean jobReady = job.isReady();
1476
1477        if (DEBUG) {
1478            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1479                    + " ready=" + jobReady);
1480        }
1481
1482        // This is a condition that is very likely to be false (most jobs that are
1483        // scheduled are sitting there, not ready yet) and very cheap to check (just
1484        // a few conditions on data in JobStatus).
1485        if (!jobReady) {
1486            return false;
1487        }
1488
1489        final boolean jobExists = mJobs.containsJob(job);
1490
1491        final int userId = job.getUserId();
1492        final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1493
1494        if (DEBUG) {
1495            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1496                    + " exists=" + jobExists + " userStarted=" + userStarted);
1497        }
1498
1499        // These are also fairly cheap to check, though they typically will not
1500        // be conditions we fail.
1501        if (!jobExists || !userStarted) {
1502            return false;
1503        }
1504
1505        final boolean jobPending = mPendingJobs.contains(job);
1506        final boolean jobActive = isCurrentlyActiveLocked(job);
1507
1508        if (DEBUG) {
1509            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1510                    + " pending=" + jobPending + " active=" + jobActive);
1511        }
1512
1513        // These can be a little more expensive (especially jobActive, since we need to
1514        // go through the array of all potentially active jobs), so we are doing them
1515        // later...  but still before checking with the package manager!
1516        if (jobPending || jobActive) {
1517            return false;
1518        }
1519
1520        final boolean componentPresent;
1521        try {
1522            componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1523                    job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1524                    userId) != null);
1525        } catch (RemoteException e) {
1526            throw e.rethrowAsRuntimeException();
1527        }
1528
1529        if (DEBUG) {
1530            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1531                    + " componentPresent=" + componentPresent);
1532        }
1533
1534        // Everything else checked out so far, so this is the final yes/no check
1535        return componentPresent;
1536    }
1537
1538    /**
1539     * Reconcile jobs in the pending queue against available execution contexts.
1540     * A controller can force a job into the pending queue even if it's already running, but
1541     * here is where we decide whether to actually execute it.
1542     */
1543    private void maybeRunPendingJobsLocked() {
1544        if (DEBUG) {
1545            Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1546        }
1547        assignJobsToContextsLocked();
1548        reportActiveLocked();
1549    }
1550
1551    private int adjustJobPriority(int curPriority, JobStatus job) {
1552        if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1553            float factor = mJobPackageTracker.getLoadFactor(job);
1554            if (factor >= mConstants.HEAVY_USE_FACTOR) {
1555                curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
1556            } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
1557                curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1558            }
1559        }
1560        return curPriority;
1561    }
1562
1563    private int evaluateJobPriorityLocked(JobStatus job) {
1564        int priority = job.getPriority();
1565        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1566            return adjustJobPriority(priority, job);
1567        }
1568        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1569        if (override != 0) {
1570            return adjustJobPriority(override, job);
1571        }
1572        return adjustJobPriority(priority, job);
1573    }
1574
1575    /**
1576     * Takes jobs from pending queue and runs them on available contexts.
1577     * If no contexts are available, preempts lower priority jobs to
1578     * run higher priority ones.
1579     * Lock on mJobs before calling this function.
1580     */
1581    private void assignJobsToContextsLocked() {
1582        if (DEBUG) {
1583            Slog.d(TAG, printPendingQueue());
1584        }
1585
1586        int memLevel;
1587        try {
1588            memLevel = ActivityManager.getService().getMemoryTrimLevel();
1589        } catch (RemoteException e) {
1590            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1591        }
1592        switch (memLevel) {
1593            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1594                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1595                break;
1596            case ProcessStats.ADJ_MEM_FACTOR_LOW:
1597                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1598                break;
1599            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1600                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1601                break;
1602            default:
1603                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1604                break;
1605        }
1606
1607        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1608        boolean[] act = mTmpAssignAct;
1609        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1610        int numActive = 0;
1611        int numForeground = 0;
1612        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1613            final JobServiceContext js = mActiveServices.get(i);
1614            final JobStatus status = js.getRunningJobLocked();
1615            if ((contextIdToJobMap[i] = status) != null) {
1616                numActive++;
1617                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1618                    numForeground++;
1619                }
1620            }
1621            act[i] = false;
1622            preferredUidForContext[i] = js.getPreferredUid();
1623        }
1624        if (DEBUG) {
1625            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1626        }
1627        for (int i=0; i<mPendingJobs.size(); i++) {
1628            JobStatus nextPending = mPendingJobs.get(i);
1629
1630            // If job is already running, go to next job.
1631            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1632            if (jobRunningContext != -1) {
1633                continue;
1634            }
1635
1636            final int priority = evaluateJobPriorityLocked(nextPending);
1637            nextPending.lastEvaluatedPriority = priority;
1638
1639            // Find a context for nextPending. The context should be available OR
1640            // it should have lowest priority among all running jobs
1641            // (sharing the same Uid as nextPending)
1642            int minPriority = Integer.MAX_VALUE;
1643            int minPriorityContextId = -1;
1644            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1645                JobStatus job = contextIdToJobMap[j];
1646                int preferredUid = preferredUidForContext[j];
1647                if (job == null) {
1648                    if ((numActive < mMaxActiveJobs ||
1649                            (priority >= JobInfo.PRIORITY_TOP_APP &&
1650                                    numForeground < mConstants.FG_JOB_COUNT)) &&
1651                            (preferredUid == nextPending.getUid() ||
1652                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1653                        // This slot is free, and we haven't yet hit the limit on
1654                        // concurrent jobs...  we can just throw the job in to here.
1655                        minPriorityContextId = j;
1656                        break;
1657                    }
1658                    // No job on this context, but nextPending can't run here because
1659                    // the context has a preferred Uid or we have reached the limit on
1660                    // concurrent jobs.
1661                    continue;
1662                }
1663                if (job.getUid() != nextPending.getUid()) {
1664                    continue;
1665                }
1666                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1667                    continue;
1668                }
1669                if (minPriority > nextPending.lastEvaluatedPriority) {
1670                    minPriority = nextPending.lastEvaluatedPriority;
1671                    minPriorityContextId = j;
1672                }
1673            }
1674            if (minPriorityContextId != -1) {
1675                contextIdToJobMap[minPriorityContextId] = nextPending;
1676                act[minPriorityContextId] = true;
1677                numActive++;
1678                if (priority >= JobInfo.PRIORITY_TOP_APP) {
1679                    numForeground++;
1680                }
1681            }
1682        }
1683        if (DEBUG) {
1684            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1685        }
1686        mJobPackageTracker.noteConcurrency(numActive, numForeground);
1687        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1688            boolean preservePreferredUid = false;
1689            if (act[i]) {
1690                JobStatus js = mActiveServices.get(i).getRunningJobLocked();
1691                if (js != null) {
1692                    if (DEBUG) {
1693                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
1694                    }
1695                    // preferredUid will be set to uid of currently running job.
1696                    mActiveServices.get(i).preemptExecutingJobLocked();
1697                    preservePreferredUid = true;
1698                } else {
1699                    final JobStatus pendingJob = contextIdToJobMap[i];
1700                    if (DEBUG) {
1701                        Slog.d(TAG, "About to run job on context "
1702                                + String.valueOf(i) + ", job: " + pendingJob);
1703                    }
1704                    for (int ic=0; ic<mControllers.size(); ic++) {
1705                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1706                    }
1707                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1708                        Slog.d(TAG, "Error executing " + pendingJob);
1709                    }
1710                    if (mPendingJobs.remove(pendingJob)) {
1711                        mJobPackageTracker.noteNonpending(pendingJob);
1712                    }
1713                }
1714            }
1715            if (!preservePreferredUid) {
1716                mActiveServices.get(i).clearPreferredUid();
1717            }
1718        }
1719    }
1720
1721    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1722        for (int i=0; i<map.length; i++) {
1723            if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1724                return i;
1725            }
1726        }
1727        return -1;
1728    }
1729
1730    final class LocalService implements JobSchedulerInternal {
1731
1732        /**
1733         * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1734         * jobs are always considered pending.
1735         */
1736        @Override
1737        public List<JobInfo> getSystemScheduledPendingJobs() {
1738            synchronized (mLock) {
1739                final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1740                mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1741                    @Override
1742                    public void process(JobStatus job) {
1743                        if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1744                            pendingJobs.add(job.getJob());
1745                        }
1746                    }
1747                });
1748                return pendingJobs;
1749            }
1750        }
1751
1752        @Override
1753        public void addBackingUpUid(int uid) {
1754            synchronized (mLock) {
1755                // No need to actually do anything here, since for a full backup the
1756                // activity manager will kill the process which will kill the job (and
1757                // cause it to restart, but now it can't run).
1758                mBackingUpUids.put(uid, uid);
1759            }
1760        }
1761
1762        @Override
1763        public void removeBackingUpUid(int uid) {
1764            synchronized (mLock) {
1765                mBackingUpUids.delete(uid);
1766                // If there are any jobs for this uid, we need to rebuild the pending list
1767                // in case they are now ready to run.
1768                if (mJobs.countJobsForUid(uid) > 0) {
1769                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1770                }
1771            }
1772        }
1773
1774        @Override
1775        public void clearAllBackingUpUids() {
1776            synchronized (mLock) {
1777                if (mBackingUpUids.size() > 0) {
1778                    mBackingUpUids.clear();
1779                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1780                }
1781            }
1782        }
1783    }
1784
1785    /**
1786     * Binder stub trampoline implementation
1787     */
1788    final class JobSchedulerStub extends IJobScheduler.Stub {
1789        /** Cache determination of whether a given app can persist jobs
1790         * key is uid of the calling app; value is undetermined/true/false
1791         */
1792        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1793
1794        // Enforce that only the app itself (or shared uid participant) can schedule a
1795        // job that runs one of the app's services, as well as verifying that the
1796        // named service properly requires the BIND_JOB_SERVICE permission
1797        private void enforceValidJobRequest(int uid, JobInfo job) {
1798            final IPackageManager pm = AppGlobals.getPackageManager();
1799            final ComponentName service = job.getService();
1800            try {
1801                ServiceInfo si = pm.getServiceInfo(service,
1802                        PackageManager.MATCH_DIRECT_BOOT_AWARE
1803                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1804                        UserHandle.getUserId(uid));
1805                if (si == null) {
1806                    throw new IllegalArgumentException("No such service " + service);
1807                }
1808                if (si.applicationInfo.uid != uid) {
1809                    throw new IllegalArgumentException("uid " + uid +
1810                            " cannot schedule job in " + service.getPackageName());
1811                }
1812                if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1813                    throw new IllegalArgumentException("Scheduled service " + service
1814                            + " does not require android.permission.BIND_JOB_SERVICE permission");
1815                }
1816            } catch (RemoteException e) {
1817                // Can't happen; the Package Manager is in this same process
1818            }
1819        }
1820
1821        private boolean canPersistJobs(int pid, int uid) {
1822            // If we get this far we're good to go; all we need to do now is check
1823            // whether the app is allowed to persist its scheduled work.
1824            final boolean canPersist;
1825            synchronized (mPersistCache) {
1826                Boolean cached = mPersistCache.get(uid);
1827                if (cached != null) {
1828                    canPersist = cached.booleanValue();
1829                } else {
1830                    // Persisting jobs is tantamount to running at boot, so we permit
1831                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1832                    // permission
1833                    int result = getContext().checkPermission(
1834                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1835                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
1836                    mPersistCache.put(uid, canPersist);
1837                }
1838            }
1839            return canPersist;
1840        }
1841
1842        // IJobScheduler implementation
1843        @Override
1844        public int schedule(JobInfo job) throws RemoteException {
1845            if (DEBUG) {
1846                Slog.d(TAG, "Scheduling job: " + job.toString());
1847            }
1848            final int pid = Binder.getCallingPid();
1849            final int uid = Binder.getCallingUid();
1850
1851            enforceValidJobRequest(uid, job);
1852            if (job.isPersisted()) {
1853                if (!canPersistJobs(pid, uid)) {
1854                    throw new IllegalArgumentException("Error: requested job be persisted without"
1855                            + " holding RECEIVE_BOOT_COMPLETED permission.");
1856                }
1857            }
1858
1859            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1860                getContext().enforceCallingOrSelfPermission(
1861                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1862            }
1863
1864            long ident = Binder.clearCallingIdentity();
1865            try {
1866                return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1867            } finally {
1868                Binder.restoreCallingIdentity(ident);
1869            }
1870        }
1871
1872        // IJobScheduler implementation
1873        @Override
1874        public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1875            if (DEBUG) {
1876                Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1877            }
1878            final int pid = Binder.getCallingPid();
1879            final int uid = Binder.getCallingUid();
1880
1881            enforceValidJobRequest(uid, job);
1882            if (job.isPersisted()) {
1883                throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1884            }
1885            if (work == null) {
1886                throw new NullPointerException("work is null");
1887            }
1888
1889            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1890                getContext().enforceCallingOrSelfPermission(
1891                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1892            }
1893
1894            long ident = Binder.clearCallingIdentity();
1895            try {
1896                return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
1897            } finally {
1898                Binder.restoreCallingIdentity(ident);
1899            }
1900        }
1901
1902        @Override
1903        public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1904                throws RemoteException {
1905            final int callerUid = Binder.getCallingUid();
1906            if (DEBUG) {
1907                Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1908                        + " on behalf of " + packageName);
1909            }
1910
1911            if (packageName == null) {
1912                throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1913            }
1914
1915            int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1916                    android.Manifest.permission.UPDATE_DEVICE_STATS);
1917            if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1918                throw new SecurityException("Caller uid " + callerUid
1919                        + " not permitted to schedule jobs for other apps");
1920            }
1921
1922            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1923                getContext().enforceCallingOrSelfPermission(
1924                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1925            }
1926
1927            long ident = Binder.clearCallingIdentity();
1928            try {
1929                return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
1930                        packageName, userId, tag);
1931            } finally {
1932                Binder.restoreCallingIdentity(ident);
1933            }
1934        }
1935
1936        @Override
1937        public List<JobInfo> getAllPendingJobs() throws RemoteException {
1938            final int uid = Binder.getCallingUid();
1939
1940            long ident = Binder.clearCallingIdentity();
1941            try {
1942                return JobSchedulerService.this.getPendingJobs(uid);
1943            } finally {
1944                Binder.restoreCallingIdentity(ident);
1945            }
1946        }
1947
1948        @Override
1949        public JobInfo getPendingJob(int jobId) throws RemoteException {
1950            final int uid = Binder.getCallingUid();
1951
1952            long ident = Binder.clearCallingIdentity();
1953            try {
1954                return JobSchedulerService.this.getPendingJob(uid, jobId);
1955            } finally {
1956                Binder.restoreCallingIdentity(ident);
1957            }
1958        }
1959
1960        @Override
1961        public void cancelAll() throws RemoteException {
1962            final int uid = Binder.getCallingUid();
1963
1964            long ident = Binder.clearCallingIdentity();
1965            try {
1966                JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
1967            } finally {
1968                Binder.restoreCallingIdentity(ident);
1969            }
1970        }
1971
1972        @Override
1973        public void cancel(int jobId) throws RemoteException {
1974            final int uid = Binder.getCallingUid();
1975
1976            long ident = Binder.clearCallingIdentity();
1977            try {
1978                JobSchedulerService.this.cancelJob(uid, jobId);
1979            } finally {
1980                Binder.restoreCallingIdentity(ident);
1981            }
1982        }
1983
1984        /**
1985         * "dumpsys" infrastructure
1986         */
1987        @Override
1988        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1989            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
1990
1991            long identityToken = Binder.clearCallingIdentity();
1992            try {
1993                JobSchedulerService.this.dumpInternal(pw, args);
1994            } finally {
1995                Binder.restoreCallingIdentity(identityToken);
1996            }
1997        }
1998
1999        @Override
2000        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2001                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
2002                (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2003                        this, in, out, err, args, callback, resultReceiver);
2004        }
2005    };
2006
2007    // Shell command infrastructure: run the given job immediately
2008    int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2009        if (DEBUG) {
2010            Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2011                    + " " + jobId + " f=" + force);
2012        }
2013
2014        try {
2015            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2016                    userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2017            if (uid < 0) {
2018                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2019            }
2020
2021            synchronized (mLock) {
2022                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2023                if (js == null) {
2024                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2025                }
2026
2027                js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2028                if (!js.isConstraintsSatisfied()) {
2029                    js.overrideState = 0;
2030                    return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2031                }
2032
2033                queueReadyJobsForExecutionLocked();
2034                maybeRunPendingJobsLocked();
2035            }
2036        } catch (RemoteException e) {
2037            // can't happen
2038        }
2039        return 0;
2040    }
2041
2042    // Shell command infrastructure: immediately timeout currently executing jobs
2043    int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2044            boolean hasJobId, int jobId) {
2045        if (DEBUG) {
2046            Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2047        }
2048
2049        synchronized (mLock) {
2050            boolean foundSome = false;
2051            for (int i=0; i<mActiveServices.size(); i++) {
2052                mActiveServices.get(i).timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId);
2053            }
2054            if (!foundSome) {
2055                pw.println("No matching executing jobs found.");
2056            }
2057        }
2058        return 0;
2059    }
2060
2061    void setMonitorBattery(boolean enabled) {
2062        synchronized (mLock) {
2063            if (mBatteryController != null) {
2064                mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2065            }
2066        }
2067    }
2068
2069    int getBatterySeq() {
2070        synchronized (mLock) {
2071            return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2072        }
2073    }
2074
2075    boolean getBatteryCharging() {
2076        synchronized (mLock) {
2077            return mBatteryController != null
2078                    ? mBatteryController.getTracker().isOnStablePower() : false;
2079        }
2080    }
2081
2082    boolean getBatteryNotLow() {
2083        synchronized (mLock) {
2084            return mBatteryController != null
2085                    ? mBatteryController.getTracker().isBatteryNotLow() : false;
2086        }
2087    }
2088
2089    int getStorageSeq() {
2090        synchronized (mLock) {
2091            return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2092        }
2093    }
2094
2095    boolean getStorageNotLow() {
2096        synchronized (mLock) {
2097            return mStorageController != null
2098                    ? mStorageController.getTracker().isStorageNotLow() : false;
2099        }
2100    }
2101
2102    int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2103        try {
2104            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2105                    userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2106            if (uid < 0) {
2107                pw.print("unknown("); pw.print(pkgName); pw.println(")");
2108                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2109            }
2110
2111            synchronized (mLock) {
2112                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2113                if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2114                if (js == null) {
2115                    pw.print("unknown("); UserHandle.formatUid(pw, uid);
2116                    pw.print("/jid"); pw.print(jobId); pw.println(")");
2117                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2118                }
2119
2120                boolean printed = false;
2121                if (mPendingJobs.contains(js)) {
2122                    pw.print("pending");
2123                    printed = true;
2124                }
2125                if (isCurrentlyActiveLocked(js)) {
2126                    if (printed) {
2127                        pw.print(" ");
2128                    }
2129                    printed = true;
2130                    pw.println("active");
2131                }
2132                if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2133                    if (printed) {
2134                        pw.print(" ");
2135                    }
2136                    printed = true;
2137                    pw.println("user-stopped");
2138                }
2139                if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2140                    if (printed) {
2141                        pw.print(" ");
2142                    }
2143                    printed = true;
2144                    pw.println("backing-up");
2145                }
2146                boolean componentPresent = false;
2147                try {
2148                    componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2149                            js.getServiceComponent(),
2150                            PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2151                            js.getUserId()) != null);
2152                } catch (RemoteException e) {
2153                }
2154                if (!componentPresent) {
2155                    if (printed) {
2156                        pw.print(" ");
2157                    }
2158                    printed = true;
2159                    pw.println("no-component");
2160                }
2161                if (js.isReady()) {
2162                    if (printed) {
2163                        pw.print(" ");
2164                    }
2165                    printed = true;
2166                    pw.println("ready");
2167                }
2168                if (!printed) {
2169                    pw.print("waiting");
2170                }
2171                pw.println();
2172            }
2173        } catch (RemoteException e) {
2174            // can't happen
2175        }
2176        return 0;
2177    }
2178
2179    private String printContextIdToJobMap(JobStatus[] map, String initial) {
2180        StringBuilder s = new StringBuilder(initial + ": ");
2181        for (int i=0; i<map.length; i++) {
2182            s.append("(")
2183                    .append(map[i] == null? -1: map[i].getJobId())
2184                    .append(map[i] == null? -1: map[i].getUid())
2185                    .append(")" );
2186        }
2187        return s.toString();
2188    }
2189
2190    private String printPendingQueue() {
2191        StringBuilder s = new StringBuilder("Pending queue: ");
2192        Iterator<JobStatus> it = mPendingJobs.iterator();
2193        while (it.hasNext()) {
2194            JobStatus js = it.next();
2195            s.append("(")
2196                    .append(js.getJob().getId())
2197                    .append(", ")
2198                    .append(js.getUid())
2199                    .append(") ");
2200        }
2201        return s.toString();
2202    }
2203
2204    static void dumpHelp(PrintWriter pw) {
2205        pw.println("Job Scheduler (jobscheduler) dump options:");
2206        pw.println("  [-h] [package] ...");
2207        pw.println("    -h: print this help");
2208        pw.println("  [package] is an optional package name to limit the output to.");
2209    }
2210
2211    void dumpInternal(final PrintWriter pw, String[] args) {
2212        int filterUid = -1;
2213        if (!ArrayUtils.isEmpty(args)) {
2214            int opti = 0;
2215            while (opti < args.length) {
2216                String arg = args[opti];
2217                if ("-h".equals(arg)) {
2218                    dumpHelp(pw);
2219                    return;
2220                } else if ("-a".equals(arg)) {
2221                    // Ignore, we always dump all.
2222                } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2223                    pw.println("Unknown option: " + arg);
2224                    return;
2225                } else {
2226                    break;
2227                }
2228                opti++;
2229            }
2230            if (opti < args.length) {
2231                String pkg = args[opti];
2232                try {
2233                    filterUid = getContext().getPackageManager().getPackageUid(pkg,
2234                            PackageManager.MATCH_ANY_USER);
2235                } catch (NameNotFoundException ignored) {
2236                    pw.println("Invalid package: " + pkg);
2237                    return;
2238                }
2239            }
2240        }
2241
2242        final int filterUidFinal = UserHandle.getAppId(filterUid);
2243        final long nowElapsed = SystemClock.elapsedRealtime();
2244        final long nowUptime = SystemClock.uptimeMillis();
2245        synchronized (mLock) {
2246            mConstants.dump(pw);
2247            pw.println();
2248            pw.println("Started users: " + Arrays.toString(mStartedUsers));
2249            pw.print("Registered ");
2250            pw.print(mJobs.size());
2251            pw.println(" jobs:");
2252            if (mJobs.size() > 0) {
2253                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2254                Collections.sort(jobs, new Comparator<JobStatus>() {
2255                    @Override
2256                    public int compare(JobStatus o1, JobStatus o2) {
2257                        int uid1 = o1.getUid();
2258                        int uid2 = o2.getUid();
2259                        int id1 = o1.getJobId();
2260                        int id2 = o2.getJobId();
2261                        if (uid1 != uid2) {
2262                            return uid1 < uid2 ? -1 : 1;
2263                        }
2264                        return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
2265                    }
2266                });
2267                for (JobStatus job : jobs) {
2268                    pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
2269                    pw.println(job.toShortStringExceptUniqueId());
2270
2271                    // Skip printing details if the caller requested a filter
2272                    if (!job.shouldDump(filterUidFinal)) {
2273                        continue;
2274                    }
2275
2276                    job.dump(pw, "    ", true, nowElapsed);
2277                    pw.print("    Ready: ");
2278                    pw.print(isReadyToBeExecutedLocked(job));
2279                    pw.print(" (job=");
2280                    pw.print(job.isReady());
2281                    pw.print(" user=");
2282                    pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
2283                    pw.print(" !pending=");
2284                    pw.print(!mPendingJobs.contains(job));
2285                    pw.print(" !active=");
2286                    pw.print(!isCurrentlyActiveLocked(job));
2287                    pw.print(" !backingup=");
2288                    pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
2289                    pw.print(" comp=");
2290                    boolean componentPresent = false;
2291                    try {
2292                        componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2293                                job.getServiceComponent(),
2294                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2295                                job.getUserId()) != null);
2296                    } catch (RemoteException e) {
2297                    }
2298                    pw.print(componentPresent);
2299                    pw.println(")");
2300                }
2301            } else {
2302                pw.println("  None.");
2303            }
2304            for (int i=0; i<mControllers.size(); i++) {
2305                pw.println();
2306                mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
2307            }
2308            pw.println();
2309            pw.println("Uid priority overrides:");
2310            for (int i=0; i< mUidPriorityOverride.size(); i++) {
2311                int uid = mUidPriorityOverride.keyAt(i);
2312                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2313                    pw.print("  "); pw.print(UserHandle.formatUid(uid));
2314                    pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2315                }
2316            }
2317            if (mBackingUpUids.size() > 0) {
2318                pw.println();
2319                pw.println("Backing up uids:");
2320                boolean first = true;
2321                for (int i = 0; i < mBackingUpUids.size(); i++) {
2322                    int uid = mBackingUpUids.keyAt(i);
2323                    if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2324                        if (first) {
2325                            pw.print("  ");
2326                            first = false;
2327                        } else {
2328                            pw.print(", ");
2329                        }
2330                        pw.print(UserHandle.formatUid(uid));
2331                    }
2332                }
2333                pw.println();
2334            }
2335            pw.println();
2336            mJobPackageTracker.dump(pw, "", filterUidFinal);
2337            pw.println();
2338            if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2339                pw.println();
2340            }
2341            pw.println("Pending queue:");
2342            for (int i=0; i<mPendingJobs.size(); i++) {
2343                JobStatus job = mPendingJobs.get(i);
2344                pw.print("  Pending #"); pw.print(i); pw.print(": ");
2345                pw.println(job.toShortString());
2346                job.dump(pw, "    ", false, nowElapsed);
2347                int priority = evaluateJobPriorityLocked(job);
2348                if (priority != JobInfo.PRIORITY_DEFAULT) {
2349                    pw.print("    Evaluated priority: "); pw.println(priority);
2350                }
2351                pw.print("    Tag: "); pw.println(job.getTag());
2352                pw.print("    Enq: ");
2353                TimeUtils.formatDuration(job.madePending - nowUptime, pw);
2354                pw.println();
2355            }
2356            pw.println();
2357            pw.println("Active jobs:");
2358            for (int i=0; i<mActiveServices.size(); i++) {
2359                JobServiceContext jsc = mActiveServices.get(i);
2360                pw.print("  Slot #"); pw.print(i); pw.print(": ");
2361                final JobStatus job = jsc.getRunningJobLocked();
2362                if (job == null) {
2363                    if (jsc.mStoppedReason != null) {
2364                        pw.print("inactive since ");
2365                        TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
2366                        pw.print(", stopped because: ");
2367                        pw.println(jsc.mStoppedReason);
2368                    } else {
2369                        pw.println("inactive");
2370                    }
2371                    continue;
2372                } else {
2373                    pw.println(job.toShortString());
2374                    pw.print("    Running for: ");
2375                    TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
2376                    pw.print(", timeout at: ");
2377                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
2378                    pw.println();
2379                    job.dump(pw, "    ", false, nowElapsed);
2380                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
2381                    if (priority != JobInfo.PRIORITY_DEFAULT) {
2382                        pw.print("    Evaluated priority: "); pw.println(priority);
2383                    }
2384                    pw.print("    Active at ");
2385                    TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
2386                    pw.print(", pending for ");
2387                    TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2388                    pw.println();
2389                }
2390            }
2391            if (filterUid == -1) {
2392                pw.println();
2393                pw.print("mReadyToRock="); pw.println(mReadyToRock);
2394                pw.print("mReportedActive="); pw.println(mReportedActive);
2395                pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2396            }
2397        }
2398        pw.println();
2399    }
2400}
2401