JobSchedulerService.java revision e9a988caca733d2f292991a52a0047685a69812f
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.ActivityManager;
32import android.app.ActivityManagerNative;
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.content.BroadcastReceiver;
41import android.content.ComponentName;
42import android.content.ContentResolver;
43import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
46import android.content.pm.IPackageManager;
47import android.content.pm.PackageManager;
48import android.content.pm.ServiceInfo;
49import android.content.pm.PackageManager.NameNotFoundException;
50import android.database.ContentObserver;
51import android.net.Uri;
52import android.os.BatteryStats;
53import android.os.Binder;
54import android.os.Handler;
55import android.os.Looper;
56import android.os.Message;
57import android.os.Process;
58import android.os.PowerManager;
59import android.os.RemoteException;
60import android.os.ResultReceiver;
61import android.os.ServiceManager;
62import android.os.SystemClock;
63import android.os.UserHandle;
64import android.provider.Settings;
65import android.util.KeyValueListParser;
66import android.util.Slog;
67import android.util.SparseArray;
68import android.util.SparseIntArray;
69import android.util.TimeUtils;
70
71import com.android.internal.app.IBatteryStats;
72import com.android.internal.app.procstats.ProcessStats;
73import com.android.internal.util.ArrayUtils;
74import com.android.server.DeviceIdleController;
75import com.android.server.LocalServices;
76import com.android.server.job.JobStore.JobStatusFunctor;
77import com.android.server.job.controllers.AppIdleController;
78import com.android.server.job.controllers.BatteryController;
79import com.android.server.job.controllers.ConnectivityController;
80import com.android.server.job.controllers.ContentObserverController;
81import com.android.server.job.controllers.DeviceIdleJobsController;
82import com.android.server.job.controllers.IdleController;
83import com.android.server.job.controllers.JobStatus;
84import com.android.server.job.controllers.StateController;
85import com.android.server.job.controllers.TimeController;
86
87import libcore.util.EmptyArray;
88
89/**
90 * Responsible for taking jobs representing work to be performed by a client app, and determining
91 * based on the criteria specified when that job should be run against the client application's
92 * endpoint.
93 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
94 * about constraints, or the state of active jobs. It receives callbacks from the various
95 * controllers and completed jobs and operates accordingly.
96 *
97 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
98 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
99 * @hide
100 */
101public final class JobSchedulerService extends com.android.server.SystemService
102        implements StateChangedListener, JobCompletedListener {
103    static final String TAG = "JobSchedulerService";
104    public static final boolean DEBUG = false;
105
106    /** The maximum number of concurrent jobs we run at one time. */
107    private static final int MAX_JOB_CONTEXTS_COUNT = 16;
108    /** Enforce a per-app limit on scheduled jobs? */
109    private static final boolean ENFORCE_MAX_JOBS = true;
110    /** The maximum number of jobs that we allow an unprivileged app to schedule */
111    private static final int MAX_JOBS_PER_APP = 100;
112
113
114    /** Global local for all job scheduler state. */
115    final Object mLock = new Object();
116    /** Master list of jobs. */
117    final JobStore mJobs;
118    /** Tracking amount of time each package runs for. */
119    final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
120
121    static final int MSG_JOB_EXPIRED = 0;
122    static final int MSG_CHECK_JOB = 1;
123    static final int MSG_STOP_JOB = 2;
124    static final int MSG_CHECK_JOB_GREEDY = 3;
125
126    /**
127     * Track Services that have currently active or pending jobs. The index is provided by
128     * {@link JobStatus#getServiceToken()}
129     */
130    final List<JobServiceContext> mActiveServices = new ArrayList<>();
131    /** List of controllers that will notify this service of updates to jobs. */
132    List<StateController> mControllers;
133    /**
134     * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
135     * when ready to execute them.
136     */
137    final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
138
139    int[] mStartedUsers = EmptyArray.INT;
140
141    final JobHandler mHandler;
142    final JobSchedulerStub mJobSchedulerStub;
143
144    IBatteryStats mBatteryStats;
145    PowerManager mPowerManager;
146    DeviceIdleController.LocalService mLocalDeviceIdleController;
147
148    /**
149     * Set to true once we are allowed to run third party apps.
150     */
151    boolean mReadyToRock;
152
153    /**
154     * What we last reported to DeviceIdleController about whether we are active.
155     */
156    boolean mReportedActive;
157
158    /**
159     * Current limit on the number of concurrent JobServiceContext entries we want to
160     * keep actively running a job.
161     */
162    int mMaxActiveJobs = 1;
163
164    /**
165     * Which uids are currently in the foreground.
166     */
167    final SparseIntArray mUidPriorityOverride = new SparseIntArray();
168
169    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
170
171    /**
172     * This array essentially stores the state of mActiveServices array.
173     * The ith index stores the job present on the ith JobServiceContext.
174     * We manipulate this array until we arrive at what jobs should be running on
175     * what JobServiceContext.
176     */
177    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
178    /**
179     * Indicates whether we need to act on this jobContext id
180     */
181    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
182    /**
183     * The uid whose jobs we would like to assign to a context.
184     */
185    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
186
187    /**
188     * All times are in milliseconds. These constants are kept synchronized with the system
189     * global Settings. Any access to this class or its fields should be done while
190     * holding the JobSchedulerService.mLock lock.
191     */
192    private final class Constants extends ContentObserver {
193        // Key names stored in the settings value.
194        private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
195        private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
196        private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
197        private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
198        private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
199        private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
200        private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
201        private static final String KEY_FG_JOB_COUNT = "fg_job_count";
202        private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
203        private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
204        private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
205        private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
206
207        private static final int DEFAULT_MIN_IDLE_COUNT = 1;
208        private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
209        private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
210        private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
211        private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
212        private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
213        private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
214        private static final int DEFAULT_FG_JOB_COUNT = 4;
215        private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
216        private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
217        private static final int DEFAULT_BG_LOW_JOB_COUNT = 2;
218        private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
219
220        /**
221         * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
222         * early.
223         */
224        int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
225        /**
226         * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
227         * things early.
228         */
229        int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
230        /**
231         * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
232         * things early.  1 == Run connectivity jobs as soon as ready.
233         */
234        int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
235        /**
236         * Minimum # of content trigger jobs that must be ready in order to force the JMS to
237         * schedule things early.
238         */
239        int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
240        /**
241         * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
242         * running some work early.  This (and thus the other min counts) is now set to 1, to
243         * prevent any batching at this level.  Since we now do batching through doze, that is
244         * a much better mechanism.
245         */
246        int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
247        /**
248         * This is the job execution factor that is considered to be heavy use of the system.
249         */
250        float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
251        /**
252         * This is the job execution factor that is considered to be moderate use of the system.
253         */
254        float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
255        /**
256         * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
257         */
258        int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
259        /**
260         * The maximum number of background jobs we allow when the system is in a normal
261         * memory state.
262         */
263        int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
264        /**
265         * The maximum number of background jobs we allow when the system is in a moderate
266         * memory state.
267         */
268        int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
269        /**
270         * The maximum number of background jobs we allow when the system is in a low
271         * memory state.
272         */
273        int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
274        /**
275         * The maximum number of background jobs we allow when the system is in a critical
276         * memory state.
277         */
278        int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
279
280        private ContentResolver mResolver;
281        private final KeyValueListParser mParser = new KeyValueListParser(',');
282
283        public Constants(Handler handler) {
284            super(handler);
285        }
286
287        public void start(ContentResolver resolver) {
288            mResolver = resolver;
289            mResolver.registerContentObserver(Settings.Global.getUriFor(
290                    Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
291            updateConstants();
292        }
293
294        @Override
295        public void onChange(boolean selfChange, Uri uri) {
296            updateConstants();
297        }
298
299        private void updateConstants() {
300            synchronized (mLock) {
301                try {
302                    mParser.setString(Settings.Global.getString(mResolver,
303                            Settings.Global.ALARM_MANAGER_CONSTANTS));
304                } catch (IllegalArgumentException e) {
305                    // Failed to parse the settings string, log this and move on
306                    // with defaults.
307                    Slog.e(TAG, "Bad device idle settings", e);
308                }
309
310                MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
311                        DEFAULT_MIN_IDLE_COUNT);
312                MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
313                        DEFAULT_MIN_CHARGING_COUNT);
314                MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
315                        DEFAULT_MIN_CONNECTIVITY_COUNT);
316                MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
317                        DEFAULT_MIN_CONTENT_COUNT);
318                MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
319                        DEFAULT_MIN_READY_JOBS_COUNT);
320                HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
321                        DEFAULT_HEAVY_USE_FACTOR);
322                MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
323                        DEFAULT_MODERATE_USE_FACTOR);
324                FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
325                        DEFAULT_FG_JOB_COUNT);
326                BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
327                        DEFAULT_BG_NORMAL_JOB_COUNT);
328                if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
329                    BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
330                }
331                BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
332                        DEFAULT_BG_MODERATE_JOB_COUNT);
333                if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
334                    BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
335                }
336                BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
337                        DEFAULT_BG_LOW_JOB_COUNT);
338                if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
339                    BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
340                }
341                BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
342                        DEFAULT_BG_CRITICAL_JOB_COUNT);
343                if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
344                    BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
345                }
346            }
347        }
348
349        void dump(PrintWriter pw) {
350            pw.println("  Settings:");
351
352            pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
353            pw.print(MIN_IDLE_COUNT); pw.println();
354
355            pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
356            pw.print(MIN_CHARGING_COUNT); pw.println();
357
358            pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
359            pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
360
361            pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
362            pw.print(MIN_CONTENT_COUNT); pw.println();
363
364            pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
365            pw.print(MIN_READY_JOBS_COUNT); pw.println();
366
367            pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
368            pw.print(HEAVY_USE_FACTOR); pw.println();
369
370            pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
371            pw.print(MODERATE_USE_FACTOR); pw.println();
372
373            pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
374            pw.print(FG_JOB_COUNT); pw.println();
375
376            pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
377            pw.print(BG_NORMAL_JOB_COUNT); pw.println();
378
379            pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
380            pw.print(BG_MODERATE_JOB_COUNT); pw.println();
381
382            pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
383            pw.print(BG_LOW_JOB_COUNT); pw.println();
384
385            pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
386            pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
387        }
388    }
389
390    final Constants mConstants;
391
392    /**
393     * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
394     * still clean up. On reinstall the package will have a new uid.
395     */
396    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
397        @Override
398        public void onReceive(Context context, Intent intent) {
399            if (DEBUG) {
400                Slog.d(TAG, "Receieved: " + intent.getAction());
401            }
402            if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
403                // Purge the app's jobs if the whole package was just disabled.  When this is
404                // the case the component name will be a bare package name.
405                final String pkgName = getPackageName(intent);
406                final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
407                if (pkgName != null && pkgUid != -1) {
408                    final String[] changedComponents = intent.getStringArrayExtra(
409                            Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
410                    if (changedComponents != null) {
411                        for (String component : changedComponents) {
412                            if (component.equals(pkgName)) {
413                                if (DEBUG) {
414                                    Slog.d(TAG, "Package state change: " + pkgName);
415                                }
416                                try {
417                                    final int userId = UserHandle.getUserId(pkgUid);
418                                    IPackageManager pm = AppGlobals.getPackageManager();
419                                    final int state = pm.getApplicationEnabledSetting(pkgName, userId);
420                                    if (state == COMPONENT_ENABLED_STATE_DISABLED
421                                            || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
422                                        if (DEBUG) {
423                                            Slog.d(TAG, "Removing jobs for package " + pkgName
424                                                    + " in user " + userId);
425                                        }
426                                        cancelJobsForUid(pkgUid, true);
427                                    }
428                                } catch (RemoteException e) { /* cannot happen */ }
429                                break;
430                            }
431                        }
432                    }
433                } else {
434                    Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
435                }
436            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
437                // If this is an outright uninstall rather than the first half of an
438                // app update sequence, cancel the jobs associated with the app.
439                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
440                    int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
441                    if (DEBUG) {
442                        Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
443                    }
444                    cancelJobsForUid(uidRemoved, true);
445                }
446            } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
447                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
448                if (DEBUG) {
449                    Slog.d(TAG, "Removing jobs for user: " + userId);
450                }
451                cancelJobsForUser(userId);
452            }
453        }
454    };
455
456    private String getPackageName(Intent intent) {
457        Uri uri = intent.getData();
458        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
459        return pkg;
460    }
461
462    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
463        @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
464            updateUidState(uid, procState);
465        }
466
467        @Override public void onUidGone(int uid) throws RemoteException {
468            updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
469        }
470
471        @Override public void onUidActive(int uid) throws RemoteException {
472        }
473
474        @Override public void onUidIdle(int uid) throws RemoteException {
475            cancelJobsForUid(uid, false);
476        }
477    };
478
479    public Object getLock() {
480        return mLock;
481    }
482
483    public JobStore getJobStore() {
484        return mJobs;
485    }
486
487    @Override
488    public void onStartUser(int userHandle) {
489        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
490        // Let's kick any outstanding jobs for this user.
491        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
492    }
493
494    @Override
495    public void onUnlockUser(int userHandle) {
496        // Let's kick any outstanding jobs for this user.
497        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
498    }
499
500    @Override
501    public void onStopUser(int userHandle) {
502        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
503    }
504
505    /**
506     * Entry point from client to schedule the provided job.
507     * This cancels the job if it's already been scheduled, and replaces it with the one provided.
508     * @param job JobInfo object containing execution parameters
509     * @param uId The package identifier of the application this job is for.
510     * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
511     */
512    public int schedule(JobInfo job, int uId) {
513        return scheduleAsPackage(job, uId, null, -1, null);
514    }
515
516    public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
517            String tag) {
518        JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
519        try {
520            if (ActivityManagerNative.getDefault().getAppStartMode(uId,
521                    job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
522                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
523                        + " -- package not allowed to start");
524                return JobScheduler.RESULT_FAILURE;
525            }
526        } catch (RemoteException e) {
527        }
528        if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
529        JobStatus toCancel;
530        synchronized (mLock) {
531            // Jobs on behalf of others don't apply to the per-app job cap
532            if (ENFORCE_MAX_JOBS && packageName == null) {
533                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
534                    Slog.w(TAG, "Too many jobs for uid " + uId);
535                    throw new IllegalStateException("Apps may not schedule more than "
536                                + MAX_JOBS_PER_APP + " distinct jobs");
537                }
538            }
539
540            toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
541            if (toCancel != null) {
542                cancelJobImpl(toCancel, jobStatus);
543            }
544            startTrackingJob(jobStatus, toCancel);
545        }
546        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
547        return JobScheduler.RESULT_SUCCESS;
548    }
549
550    public List<JobInfo> getPendingJobs(int uid) {
551        synchronized (mLock) {
552            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
553            ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
554            for (int i = jobs.size() - 1; i >= 0; i--) {
555                JobStatus job = jobs.get(i);
556                outList.add(job.getJob());
557            }
558            return outList;
559        }
560    }
561
562    public JobInfo getPendingJob(int uid, int jobId) {
563        synchronized (mLock) {
564            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
565            for (int i = jobs.size() - 1; i >= 0; i--) {
566                JobStatus job = jobs.get(i);
567                if (job.getJobId() == jobId) {
568                    return job.getJob();
569                }
570            }
571            return null;
572        }
573    }
574
575    void cancelJobsForUser(int userHandle) {
576        List<JobStatus> jobsForUser;
577        synchronized (mLock) {
578            jobsForUser = mJobs.getJobsByUser(userHandle);
579        }
580        for (int i=0; i<jobsForUser.size(); i++) {
581            JobStatus toRemove = jobsForUser.get(i);
582            cancelJobImpl(toRemove, null);
583        }
584    }
585
586    /**
587     * Entry point from client to cancel all jobs originating from their uid.
588     * This will remove the job from the master list, and cancel the job if it was staged for
589     * execution or being executed.
590     * @param uid Uid to check against for removal of a job.
591     * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
592     * whose apps are stopped.
593     */
594    public void cancelJobsForUid(int uid, boolean forceAll) {
595        List<JobStatus> jobsForUid;
596        synchronized (mLock) {
597            jobsForUid = mJobs.getJobsByUid(uid);
598        }
599        for (int i=0; i<jobsForUid.size(); i++) {
600            JobStatus toRemove = jobsForUid.get(i);
601            if (!forceAll) {
602                String packageName = toRemove.getServiceComponent().getPackageName();
603                try {
604                    if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
605                            != ActivityManager.APP_START_MODE_DISABLED) {
606                        continue;
607                    }
608                } catch (RemoteException e) {
609                }
610            }
611            cancelJobImpl(toRemove, null);
612        }
613    }
614
615    /**
616     * Entry point from client to cancel the job corresponding to the jobId provided.
617     * This will remove the job from the master list, and cancel the job if it was staged for
618     * execution or being executed.
619     * @param uid Uid of the calling client.
620     * @param jobId Id of the job, provided at schedule-time.
621     */
622    public void cancelJob(int uid, int jobId) {
623        JobStatus toCancel;
624        synchronized (mLock) {
625            toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
626        }
627        if (toCancel != null) {
628            cancelJobImpl(toCancel, null);
629        }
630    }
631
632    private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
633        if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
634        stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
635        synchronized (mLock) {
636            // Remove from pending queue.
637            if (mPendingJobs.remove(cancelled)) {
638                mJobPackageTracker.noteNonpending(cancelled);
639            }
640            // Cancel if running.
641            stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
642            reportActive();
643        }
644    }
645
646    void updateUidState(int uid, int procState) {
647        synchronized (mLock) {
648            if (procState == ActivityManager.PROCESS_STATE_TOP) {
649                // Only use this if we are exactly the top app.  All others can live
650                // with just the foreground priority.  This means that persistent processes
651                // can never be the top app priority...  that is fine.
652                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
653            } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
654                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
655            } else {
656                mUidPriorityOverride.delete(uid);
657            }
658        }
659    }
660
661    @Override
662    public void onDeviceIdleStateChanged(boolean deviceIdle) {
663        synchronized (mLock) {
664            if (deviceIdle) {
665                // When becoming idle, make sure no jobs are actively running.
666                for (int i=0; i<mActiveServices.size(); i++) {
667                    JobServiceContext jsc = mActiveServices.get(i);
668                    final JobStatus executing = jsc.getRunningJob();
669                    if (executing != null) {
670                        jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
671                    }
672                }
673            } else {
674                // When coming out of idle, allow thing to start back up.
675                if (mReadyToRock) {
676                    if (mLocalDeviceIdleController != null) {
677                        if (!mReportedActive) {
678                            mReportedActive = true;
679                            mLocalDeviceIdleController.setJobsActive(true);
680                        }
681                    }
682                }
683                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
684            }
685        }
686    }
687
688    void reportActive() {
689        // active is true if pending queue contains jobs OR some job is running.
690        boolean active = mPendingJobs.size() > 0;
691        if (mPendingJobs.size() <= 0) {
692            for (int i=0; i<mActiveServices.size(); i++) {
693                JobServiceContext jsc = mActiveServices.get(i);
694                if (jsc.getRunningJob() != null) {
695                    active = true;
696                    break;
697                }
698            }
699        }
700
701        if (mReportedActive != active) {
702            mReportedActive = active;
703            if (mLocalDeviceIdleController != null) {
704                mLocalDeviceIdleController.setJobsActive(active);
705            }
706        }
707    }
708
709    /**
710     * Initializes the system service.
711     * <p>
712     * Subclasses must define a single argument constructor that accepts the context
713     * and passes it to super.
714     * </p>
715     *
716     * @param context The system server context.
717     */
718    public JobSchedulerService(Context context) {
719        super(context);
720        // Create the controllers.
721        mControllers = new ArrayList<StateController>();
722        mControllers.add(ConnectivityController.get(this));
723        mControllers.add(TimeController.get(this));
724        mControllers.add(IdleController.get(this));
725        mControllers.add(BatteryController.get(this));
726        mControllers.add(AppIdleController.get(this));
727        mControllers.add(ContentObserverController.get(this));
728        mControllers.add(DeviceIdleJobsController.get(this));
729
730        mHandler = new JobHandler(context.getMainLooper());
731        mConstants = new Constants(mHandler);
732        mJobSchedulerStub = new JobSchedulerStub();
733        mJobs = JobStore.initAndGet(this);
734    }
735
736    @Override
737    public void onStart() {
738        publishLocalService(JobSchedulerInternal.class, new LocalService());
739        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
740    }
741
742    @Override
743    public void onBootPhase(int phase) {
744        if (PHASE_SYSTEM_SERVICES_READY == phase) {
745            mConstants.start(getContext().getContentResolver());
746            // Register br for package removals and user removals.
747            final IntentFilter filter = new IntentFilter();
748            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
749            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
750            filter.addDataScheme("package");
751            getContext().registerReceiverAsUser(
752                    mBroadcastReceiver, UserHandle.ALL, filter, null, null);
753            final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
754            getContext().registerReceiverAsUser(
755                    mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
756            mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
757            try {
758                ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
759                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
760                        | ActivityManager.UID_OBSERVER_IDLE);
761            } catch (RemoteException e) {
762                // ignored; both services live in system_server
763            }
764        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
765            synchronized (mLock) {
766                // Let's go!
767                mReadyToRock = true;
768                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
769                        BatteryStats.SERVICE_NAME));
770                mLocalDeviceIdleController
771                        = LocalServices.getService(DeviceIdleController.LocalService.class);
772                // Create the "runners".
773                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
774                    mActiveServices.add(
775                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
776                                    getContext().getMainLooper()));
777                }
778                // Attach jobs to their controllers.
779                mJobs.forEachJob(new JobStatusFunctor() {
780                    @Override
781                    public void process(JobStatus job) {
782                        for (int controller = 0; controller < mControllers.size(); controller++) {
783                            final StateController sc = mControllers.get(controller);
784                            sc.maybeStartTrackingJobLocked(job, null);
785                        }
786                    }
787                });
788                // GO GO GO!
789                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
790            }
791        }
792    }
793
794    /**
795     * Called when we have a job status object that we need to insert in our
796     * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
797     * about.
798     */
799    private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
800        synchronized (mLock) {
801            final boolean update = mJobs.add(jobStatus);
802            if (mReadyToRock) {
803                for (int i = 0; i < mControllers.size(); i++) {
804                    StateController controller = mControllers.get(i);
805                    if (update) {
806                        controller.maybeStopTrackingJobLocked(jobStatus, null, true);
807                    }
808                    controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
809                }
810            }
811        }
812    }
813
814    /**
815     * Called when we want to remove a JobStatus object that we've finished executing. Returns the
816     * object removed.
817     */
818    private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
819            boolean writeBack) {
820        synchronized (mLock) {
821            // Remove from store as well as controllers.
822            final boolean removed = mJobs.remove(jobStatus, writeBack);
823            if (removed && mReadyToRock) {
824                for (int i=0; i<mControllers.size(); i++) {
825                    StateController controller = mControllers.get(i);
826                    controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
827                }
828            }
829            return removed;
830        }
831    }
832
833    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
834        for (int i=0; i<mActiveServices.size(); i++) {
835            JobServiceContext jsc = mActiveServices.get(i);
836            final JobStatus executing = jsc.getRunningJob();
837            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
838                jsc.cancelExecutingJob(reason);
839                return true;
840            }
841        }
842        return false;
843    }
844
845    /**
846     * @param job JobStatus we are querying against.
847     * @return Whether or not the job represented by the status object is currently being run or
848     * is pending.
849     */
850    private boolean isCurrentlyActiveLocked(JobStatus job) {
851        for (int i=0; i<mActiveServices.size(); i++) {
852            JobServiceContext serviceContext = mActiveServices.get(i);
853            final JobStatus running = serviceContext.getRunningJob();
854            if (running != null && running.matches(job.getUid(), job.getJobId())) {
855                return true;
856            }
857        }
858        return false;
859    }
860
861    void noteJobsPending(List<JobStatus> jobs) {
862        for (int i = jobs.size() - 1; i >= 0; i--) {
863            JobStatus job = jobs.get(i);
864            mJobPackageTracker.notePending(job);
865        }
866    }
867
868    void noteJobsNonpending(List<JobStatus> jobs) {
869        for (int i = jobs.size() - 1; i >= 0; i--) {
870            JobStatus job = jobs.get(i);
871            mJobPackageTracker.noteNonpending(job);
872        }
873    }
874
875    /**
876     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
877     * specify an override deadline on a failed job (the failed job will run even though it's not
878     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
879     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
880     *
881     * @param failureToReschedule Provided job status that we will reschedule.
882     * @return A newly instantiated JobStatus with the same constraints as the last job except
883     * with adjusted timing constraints.
884     *
885     * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
886     */
887    private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
888        final long elapsedNowMillis = SystemClock.elapsedRealtime();
889        final JobInfo job = failureToReschedule.getJob();
890
891        final long initialBackoffMillis = job.getInitialBackoffMillis();
892        final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
893        long delayMillis;
894
895        switch (job.getBackoffPolicy()) {
896            case JobInfo.BACKOFF_POLICY_LINEAR:
897                delayMillis = initialBackoffMillis * backoffAttempts;
898                break;
899            default:
900                if (DEBUG) {
901                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
902                }
903            case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
904                delayMillis =
905                        (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
906                break;
907        }
908        delayMillis =
909                Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
910        JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
911                JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
912        for (int ic=0; ic<mControllers.size(); ic++) {
913            StateController controller = mControllers.get(ic);
914            controller.rescheduleForFailure(newJob, failureToReschedule);
915        }
916        return newJob;
917    }
918
919    /**
920     * Called after a periodic has executed so we can reschedule it. We take the last execution
921     * time of the job to be the time of completion (i.e. the time at which this function is
922     * called).
923     * This could be inaccurate b/c the job can run for as long as
924     * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
925     * to underscheduling at least, rather than if we had taken the last execution time to be the
926     * start of the execution.
927     * @return A new job representing the execution criteria for this instantiation of the
928     * recurring job.
929     */
930    private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
931        final long elapsedNow = SystemClock.elapsedRealtime();
932        // Compute how much of the period is remaining.
933        long runEarly = 0L;
934
935        // If this periodic was rescheduled it won't have a deadline.
936        if (periodicToReschedule.hasDeadlineConstraint()) {
937            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
938        }
939        long flex = periodicToReschedule.getJob().getFlexMillis();
940        long period = periodicToReschedule.getJob().getIntervalMillis();
941        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
942        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
943
944        if (DEBUG) {
945            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
946                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
947        }
948        return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
949                newLatestRuntimeElapsed, 0 /* backoffAttempt */);
950    }
951
952    // JobCompletedListener implementations.
953
954    /**
955     * A job just finished executing. We fetch the
956     * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
957     * whether we want to reschedule we readd it to the controllers.
958     * @param jobStatus Completed job.
959     * @param needsReschedule Whether the implementing class should reschedule this job.
960     */
961    @Override
962    public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
963        if (DEBUG) {
964            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
965        }
966        // Do not write back immediately if this is a periodic job. The job may get lost if system
967        // shuts down before it is added back.
968        if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
969            if (DEBUG) {
970                Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
971            }
972            // We still want to check for jobs to execute, because this job may have
973            // scheduled a new job under the same job id, and now we can run it.
974            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
975            return;
976        }
977        // Note: there is a small window of time in here where, when rescheduling a job,
978        // we will stop monitoring its content providers.  This should be fixed by stopping
979        // the old job after scheduling the new one, but since we have no lock held here
980        // that may cause ordering problems if the app removes jobStatus while in here.
981        if (needsReschedule) {
982            JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
983            startTrackingJob(rescheduled, jobStatus);
984        } else if (jobStatus.getJob().isPeriodic()) {
985            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
986            startTrackingJob(rescheduledPeriodic, jobStatus);
987        }
988        reportActive();
989        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
990    }
991
992    // StateChangedListener implementations.
993
994    /**
995     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
996     * some controller's state has changed, so as to run through the list of jobs and start/stop
997     * any that are eligible.
998     */
999    @Override
1000    public void onControllerStateChanged() {
1001        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1002    }
1003
1004    @Override
1005    public void onRunJobNow(JobStatus jobStatus) {
1006        mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1007    }
1008
1009    private class JobHandler extends Handler {
1010
1011        public JobHandler(Looper looper) {
1012            super(looper);
1013        }
1014
1015        @Override
1016        public void handleMessage(Message message) {
1017            synchronized (mLock) {
1018                if (!mReadyToRock) {
1019                    return;
1020                }
1021            }
1022            switch (message.what) {
1023                case MSG_JOB_EXPIRED:
1024                    synchronized (mLock) {
1025                        JobStatus runNow = (JobStatus) message.obj;
1026                        // runNow can be null, which is a controller's way of indicating that its
1027                        // state is such that all ready jobs should be run immediately.
1028                        if (runNow != null && !mPendingJobs.contains(runNow)
1029                                && mJobs.containsJob(runNow)) {
1030                            mJobPackageTracker.notePending(runNow);
1031                            mPendingJobs.add(runNow);
1032                        }
1033                        queueReadyJobsForExecutionLockedH();
1034                    }
1035                    break;
1036                case MSG_CHECK_JOB:
1037                    synchronized (mLock) {
1038                        if (mReportedActive) {
1039                            // if jobs are currently being run, queue all ready jobs for execution.
1040                            queueReadyJobsForExecutionLockedH();
1041                        } else {
1042                            // Check the list of jobs and run some of them if we feel inclined.
1043                            maybeQueueReadyJobsForExecutionLockedH();
1044                        }
1045                    }
1046                    break;
1047                case MSG_CHECK_JOB_GREEDY:
1048                    synchronized (mLock) {
1049                        queueReadyJobsForExecutionLockedH();
1050                    }
1051                    break;
1052                case MSG_STOP_JOB:
1053                    cancelJobImpl((JobStatus)message.obj, null);
1054                    break;
1055            }
1056            maybeRunPendingJobsH();
1057            // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1058            removeMessages(MSG_CHECK_JOB);
1059        }
1060
1061        /**
1062         * Run through list of jobs and execute all possible - at least one is expired so we do
1063         * as many as we can.
1064         */
1065        private void queueReadyJobsForExecutionLockedH() {
1066            if (DEBUG) {
1067                Slog.d(TAG, "queuing all ready jobs for execution:");
1068            }
1069            noteJobsNonpending(mPendingJobs);
1070            mPendingJobs.clear();
1071            mJobs.forEachJob(mReadyQueueFunctor);
1072            mReadyQueueFunctor.postProcess();
1073
1074            if (DEBUG) {
1075                final int queuedJobs = mPendingJobs.size();
1076                if (queuedJobs == 0) {
1077                    Slog.d(TAG, "No jobs pending.");
1078                } else {
1079                    Slog.d(TAG, queuedJobs + " jobs queued.");
1080                }
1081            }
1082        }
1083
1084        class ReadyJobQueueFunctor implements JobStatusFunctor {
1085            ArrayList<JobStatus> newReadyJobs;
1086
1087            @Override
1088            public void process(JobStatus job) {
1089                if (isReadyToBeExecutedLocked(job)) {
1090                    if (DEBUG) {
1091                        Slog.d(TAG, "    queued " + job.toShortString());
1092                    }
1093                    if (newReadyJobs == null) {
1094                        newReadyJobs = new ArrayList<JobStatus>();
1095                    }
1096                    newReadyJobs.add(job);
1097                } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1098                    stopJobOnServiceContextLocked(job,
1099                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1100                }
1101            }
1102
1103            public void postProcess() {
1104                if (newReadyJobs != null) {
1105                    noteJobsPending(newReadyJobs);
1106                    mPendingJobs.addAll(newReadyJobs);
1107                }
1108                newReadyJobs = null;
1109            }
1110        }
1111        private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1112
1113        /**
1114         * The state of at least one job has changed. Here is where we could enforce various
1115         * policies on when we want to execute jobs.
1116         * Right now the policy is such:
1117         * If >1 of the ready jobs is idle mode we send all of them off
1118         * if more than 2 network connectivity jobs are ready we send them all off.
1119         * If more than 4 jobs total are ready we send them all off.
1120         * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1121         */
1122        class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1123            int chargingCount;
1124            int idleCount;
1125            int backoffCount;
1126            int connectivityCount;
1127            int contentCount;
1128            List<JobStatus> runnableJobs;
1129
1130            public MaybeReadyJobQueueFunctor() {
1131                reset();
1132            }
1133
1134            // Functor method invoked for each job via JobStore.forEachJob()
1135            @Override
1136            public void process(JobStatus job) {
1137                if (isReadyToBeExecutedLocked(job)) {
1138                    try {
1139                        if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
1140                                job.getJob().getService().getPackageName())
1141                                == ActivityManager.APP_START_MODE_DISABLED) {
1142                            Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1143                                    + job.getJob().toString() + " -- package not allowed to start");
1144                            mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1145                            return;
1146                        }
1147                    } catch (RemoteException e) {
1148                    }
1149                    if (job.getNumFailures() > 0) {
1150                        backoffCount++;
1151                    }
1152                    if (job.hasIdleConstraint()) {
1153                        idleCount++;
1154                    }
1155                    if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1156                            || job.hasNotRoamingConstraint()) {
1157                        connectivityCount++;
1158                    }
1159                    if (job.hasChargingConstraint()) {
1160                        chargingCount++;
1161                    }
1162                    if (job.hasContentTriggerConstraint()) {
1163                        contentCount++;
1164                    }
1165                    if (runnableJobs == null) {
1166                        runnableJobs = new ArrayList<>();
1167                    }
1168                    runnableJobs.add(job);
1169                } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1170                    stopJobOnServiceContextLocked(job,
1171                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1172                }
1173            }
1174
1175            public void postProcess() {
1176                if (backoffCount > 0 ||
1177                        idleCount >= mConstants.MIN_IDLE_COUNT ||
1178                        connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1179                        chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1180                        contentCount >= mConstants.MIN_CONTENT_COUNT ||
1181                        (runnableJobs != null
1182                                && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1183                    if (DEBUG) {
1184                        Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1185                    }
1186                    noteJobsPending(runnableJobs);
1187                    mPendingJobs.addAll(runnableJobs);
1188                } else {
1189                    if (DEBUG) {
1190                        Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1191                    }
1192                }
1193
1194                // Be ready for next time
1195                reset();
1196            }
1197
1198            private void reset() {
1199                chargingCount = 0;
1200                idleCount =  0;
1201                backoffCount = 0;
1202                connectivityCount = 0;
1203                contentCount = 0;
1204                runnableJobs = null;
1205            }
1206        }
1207        private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1208
1209        private void maybeQueueReadyJobsForExecutionLockedH() {
1210            if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1211
1212            noteJobsNonpending(mPendingJobs);
1213            mPendingJobs.clear();
1214            mJobs.forEachJob(mMaybeQueueFunctor);
1215            mMaybeQueueFunctor.postProcess();
1216        }
1217
1218        /**
1219         * Criteria for moving a job into the pending queue:
1220         *      - It's ready.
1221         *      - It's not pending.
1222         *      - It's not already running on a JSC.
1223         *      - The user that requested the job is running.
1224         *      - The component is enabled and runnable.
1225         */
1226        private boolean isReadyToBeExecutedLocked(JobStatus job) {
1227            final boolean jobReady = job.isReady();
1228            final boolean jobPending = mPendingJobs.contains(job);
1229            final boolean jobActive = isCurrentlyActiveLocked(job);
1230
1231            final int userId = job.getUserId();
1232            final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1233            final boolean componentPresent;
1234            try {
1235                componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1236                        job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1237                        userId) != null);
1238            } catch (RemoteException e) {
1239                throw e.rethrowAsRuntimeException();
1240            }
1241
1242            if (DEBUG) {
1243                Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1244                        + " ready=" + jobReady + " pending=" + jobPending
1245                        + " active=" + jobActive + " userStarted=" + userStarted
1246                        + " componentPresent=" + componentPresent);
1247            }
1248            return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
1249        }
1250
1251        /**
1252         * Criteria for cancelling an active job:
1253         *      - It's not ready
1254         *      - It's running on a JSC.
1255         */
1256        private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
1257            return !job.isReady() && isCurrentlyActiveLocked(job);
1258        }
1259
1260        /**
1261         * Reconcile jobs in the pending queue against available execution contexts.
1262         * A controller can force a job into the pending queue even if it's already running, but
1263         * here is where we decide whether to actually execute it.
1264         */
1265        private void maybeRunPendingJobsH() {
1266            synchronized (mLock) {
1267                if (DEBUG) {
1268                    Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1269                }
1270                assignJobsToContextsLocked();
1271                reportActive();
1272            }
1273        }
1274    }
1275
1276    private int adjustJobPriority(int curPriority, JobStatus job) {
1277        if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1278            float factor = mJobPackageTracker.getLoadFactor(job);
1279            if (factor >= mConstants.HEAVY_USE_FACTOR) {
1280                curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
1281            } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
1282                curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1283            }
1284        }
1285        return curPriority;
1286    }
1287
1288    private int evaluateJobPriorityLocked(JobStatus job) {
1289        int priority = job.getPriority();
1290        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1291            return adjustJobPriority(priority, job);
1292        }
1293        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1294        if (override != 0) {
1295            return adjustJobPriority(override, job);
1296        }
1297        return adjustJobPriority(priority, job);
1298    }
1299
1300    /**
1301     * Takes jobs from pending queue and runs them on available contexts.
1302     * If no contexts are available, preempts lower priority jobs to
1303     * run higher priority ones.
1304     * Lock on mJobs before calling this function.
1305     */
1306    private void assignJobsToContextsLocked() {
1307        if (DEBUG) {
1308            Slog.d(TAG, printPendingQueue());
1309        }
1310
1311        int memLevel;
1312        try {
1313            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
1314        } catch (RemoteException e) {
1315            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1316        }
1317        switch (memLevel) {
1318            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1319                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1320                break;
1321            case ProcessStats.ADJ_MEM_FACTOR_LOW:
1322                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1323                break;
1324            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1325                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1326                break;
1327            default:
1328                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1329                break;
1330        }
1331
1332        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1333        boolean[] act = mTmpAssignAct;
1334        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1335        int numActive = 0;
1336        int numForeground = 0;
1337        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1338            final JobServiceContext js = mActiveServices.get(i);
1339            final JobStatus status = js.getRunningJob();
1340            if ((contextIdToJobMap[i] = status) != null) {
1341                numActive++;
1342                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1343                    numForeground++;
1344                }
1345            }
1346            act[i] = false;
1347            preferredUidForContext[i] = js.getPreferredUid();
1348        }
1349        if (DEBUG) {
1350            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1351        }
1352        for (int i=0; i<mPendingJobs.size(); i++) {
1353            JobStatus nextPending = mPendingJobs.get(i);
1354
1355            // If job is already running, go to next job.
1356            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1357            if (jobRunningContext != -1) {
1358                continue;
1359            }
1360
1361            final int priority = evaluateJobPriorityLocked(nextPending);
1362            nextPending.lastEvaluatedPriority = priority;
1363
1364            // Find a context for nextPending. The context should be available OR
1365            // it should have lowest priority among all running jobs
1366            // (sharing the same Uid as nextPending)
1367            int minPriority = Integer.MAX_VALUE;
1368            int minPriorityContextId = -1;
1369            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1370                JobStatus job = contextIdToJobMap[j];
1371                int preferredUid = preferredUidForContext[j];
1372                if (job == null) {
1373                    if ((numActive < mMaxActiveJobs ||
1374                            (priority >= JobInfo.PRIORITY_TOP_APP &&
1375                                    numForeground < mConstants.FG_JOB_COUNT)) &&
1376                            (preferredUid == nextPending.getUid() ||
1377                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1378                        // This slot is free, and we haven't yet hit the limit on
1379                        // concurrent jobs...  we can just throw the job in to here.
1380                        minPriorityContextId = j;
1381                        break;
1382                    }
1383                    // No job on this context, but nextPending can't run here because
1384                    // the context has a preferred Uid or we have reached the limit on
1385                    // concurrent jobs.
1386                    continue;
1387                }
1388                if (job.getUid() != nextPending.getUid()) {
1389                    continue;
1390                }
1391                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1392                    continue;
1393                }
1394                if (minPriority > nextPending.lastEvaluatedPriority) {
1395                    minPriority = nextPending.lastEvaluatedPriority;
1396                    minPriorityContextId = j;
1397                }
1398            }
1399            if (minPriorityContextId != -1) {
1400                contextIdToJobMap[minPriorityContextId] = nextPending;
1401                act[minPriorityContextId] = true;
1402                numActive++;
1403                if (priority >= JobInfo.PRIORITY_TOP_APP) {
1404                    numForeground++;
1405                }
1406            }
1407        }
1408        if (DEBUG) {
1409            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1410        }
1411        mJobPackageTracker.noteConcurrency(numActive, numForeground);
1412        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1413            boolean preservePreferredUid = false;
1414            if (act[i]) {
1415                JobStatus js = mActiveServices.get(i).getRunningJob();
1416                if (js != null) {
1417                    if (DEBUG) {
1418                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1419                    }
1420                    // preferredUid will be set to uid of currently running job.
1421                    mActiveServices.get(i).preemptExecutingJob();
1422                    preservePreferredUid = true;
1423                } else {
1424                    final JobStatus pendingJob = contextIdToJobMap[i];
1425                    if (DEBUG) {
1426                        Slog.d(TAG, "About to run job on context "
1427                                + String.valueOf(i) + ", job: " + pendingJob);
1428                    }
1429                    for (int ic=0; ic<mControllers.size(); ic++) {
1430                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1431                    }
1432                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1433                        Slog.d(TAG, "Error executing " + pendingJob);
1434                    }
1435                    if (mPendingJobs.remove(pendingJob)) {
1436                        mJobPackageTracker.noteNonpending(pendingJob);
1437                    }
1438                }
1439            }
1440            if (!preservePreferredUid) {
1441                mActiveServices.get(i).clearPreferredUid();
1442            }
1443        }
1444    }
1445
1446    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1447        for (int i=0; i<map.length; i++) {
1448            if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1449                return i;
1450            }
1451        }
1452        return -1;
1453    }
1454
1455    final class LocalService implements JobSchedulerInternal {
1456
1457        /**
1458         * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1459         * jobs are always considered pending.
1460         */
1461        @Override
1462        public List<JobInfo> getSystemScheduledPendingJobs() {
1463            synchronized (mLock) {
1464                final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1465                mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1466                    @Override
1467                    public void process(JobStatus job) {
1468                        if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1469                            pendingJobs.add(job.getJob());
1470                        }
1471                    }
1472                });
1473                return pendingJobs;
1474            }
1475        }
1476    }
1477
1478    /**
1479     * Binder stub trampoline implementation
1480     */
1481    final class JobSchedulerStub extends IJobScheduler.Stub {
1482        /** Cache determination of whether a given app can persist jobs
1483         * key is uid of the calling app; value is undetermined/true/false
1484         */
1485        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1486
1487        // Enforce that only the app itself (or shared uid participant) can schedule a
1488        // job that runs one of the app's services, as well as verifying that the
1489        // named service properly requires the BIND_JOB_SERVICE permission
1490        private void enforceValidJobRequest(int uid, JobInfo job) {
1491            final IPackageManager pm = AppGlobals.getPackageManager();
1492            final ComponentName service = job.getService();
1493            try {
1494                ServiceInfo si = pm.getServiceInfo(service,
1495                        PackageManager.MATCH_DIRECT_BOOT_AWARE
1496                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1497                        UserHandle.getUserId(uid));
1498                if (si == null) {
1499                    throw new IllegalArgumentException("No such service " + service);
1500                }
1501                if (si.applicationInfo.uid != uid) {
1502                    throw new IllegalArgumentException("uid " + uid +
1503                            " cannot schedule job in " + service.getPackageName());
1504                }
1505                if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1506                    throw new IllegalArgumentException("Scheduled service " + service
1507                            + " does not require android.permission.BIND_JOB_SERVICE permission");
1508                }
1509            } catch (RemoteException e) {
1510                // Can't happen; the Package Manager is in this same process
1511            }
1512        }
1513
1514        private boolean canPersistJobs(int pid, int uid) {
1515            // If we get this far we're good to go; all we need to do now is check
1516            // whether the app is allowed to persist its scheduled work.
1517            final boolean canPersist;
1518            synchronized (mPersistCache) {
1519                Boolean cached = mPersistCache.get(uid);
1520                if (cached != null) {
1521                    canPersist = cached.booleanValue();
1522                } else {
1523                    // Persisting jobs is tantamount to running at boot, so we permit
1524                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1525                    // permission
1526                    int result = getContext().checkPermission(
1527                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1528                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
1529                    mPersistCache.put(uid, canPersist);
1530                }
1531            }
1532            return canPersist;
1533        }
1534
1535        // IJobScheduler implementation
1536        @Override
1537        public int schedule(JobInfo job) throws RemoteException {
1538            if (DEBUG) {
1539                Slog.d(TAG, "Scheduling job: " + job.toString());
1540            }
1541            final int pid = Binder.getCallingPid();
1542            final int uid = Binder.getCallingUid();
1543
1544            enforceValidJobRequest(uid, job);
1545            if (job.isPersisted()) {
1546                if (!canPersistJobs(pid, uid)) {
1547                    throw new IllegalArgumentException("Error: requested job be persisted without"
1548                            + " holding RECEIVE_BOOT_COMPLETED permission.");
1549                }
1550            }
1551
1552            long ident = Binder.clearCallingIdentity();
1553            try {
1554                return JobSchedulerService.this.schedule(job, uid);
1555            } finally {
1556                Binder.restoreCallingIdentity(ident);
1557            }
1558        }
1559
1560        @Override
1561        public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1562                throws RemoteException {
1563            final int callerUid = Binder.getCallingUid();
1564            if (DEBUG) {
1565                Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1566                        + " on behalf of " + packageName);
1567            }
1568
1569            if (packageName == null) {
1570                throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1571            }
1572
1573            int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1574                    android.Manifest.permission.UPDATE_DEVICE_STATS);
1575            if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1576                throw new SecurityException("Caller uid " + callerUid
1577                        + " not permitted to schedule jobs for other apps");
1578            }
1579
1580            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1581                getContext().enforceCallingOrSelfPermission(
1582                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1583            }
1584
1585            long ident = Binder.clearCallingIdentity();
1586            try {
1587                return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
1588                        packageName, userId, tag);
1589            } finally {
1590                Binder.restoreCallingIdentity(ident);
1591            }
1592        }
1593
1594        @Override
1595        public List<JobInfo> getAllPendingJobs() throws RemoteException {
1596            final int uid = Binder.getCallingUid();
1597
1598            long ident = Binder.clearCallingIdentity();
1599            try {
1600                return JobSchedulerService.this.getPendingJobs(uid);
1601            } finally {
1602                Binder.restoreCallingIdentity(ident);
1603            }
1604        }
1605
1606        @Override
1607        public JobInfo getPendingJob(int jobId) throws RemoteException {
1608            final int uid = Binder.getCallingUid();
1609
1610            long ident = Binder.clearCallingIdentity();
1611            try {
1612                return JobSchedulerService.this.getPendingJob(uid, jobId);
1613            } finally {
1614                Binder.restoreCallingIdentity(ident);
1615            }
1616        }
1617
1618        @Override
1619        public void cancelAll() throws RemoteException {
1620            final int uid = Binder.getCallingUid();
1621
1622            long ident = Binder.clearCallingIdentity();
1623            try {
1624                JobSchedulerService.this.cancelJobsForUid(uid, true);
1625            } finally {
1626                Binder.restoreCallingIdentity(ident);
1627            }
1628        }
1629
1630        @Override
1631        public void cancel(int jobId) throws RemoteException {
1632            final int uid = Binder.getCallingUid();
1633
1634            long ident = Binder.clearCallingIdentity();
1635            try {
1636                JobSchedulerService.this.cancelJob(uid, jobId);
1637            } finally {
1638                Binder.restoreCallingIdentity(ident);
1639            }
1640        }
1641
1642        /**
1643         * "dumpsys" infrastructure
1644         */
1645        @Override
1646        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1647            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1648
1649            long identityToken = Binder.clearCallingIdentity();
1650            try {
1651                JobSchedulerService.this.dumpInternal(pw, args);
1652            } finally {
1653                Binder.restoreCallingIdentity(identityToken);
1654            }
1655        }
1656
1657        @Override
1658        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1659                String[] args, ResultReceiver resultReceiver) throws RemoteException {
1660                (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
1661                        this, in, out, err, args, resultReceiver);
1662        }
1663    };
1664
1665    // Shell command infrastructure: run the given job immediately
1666    int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1667        if (DEBUG) {
1668            Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1669                    + " " + jobId + " f=" + force);
1670        }
1671
1672        try {
1673            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1674            if (uid < 0) {
1675                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1676            }
1677
1678            synchronized (mLock) {
1679                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1680                if (js == null) {
1681                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1682                }
1683
1684                js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1685                if (!js.isConstraintsSatisfied()) {
1686                    js.overrideState = 0;
1687                    return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1688                }
1689
1690                mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1691            }
1692        } catch (RemoteException e) {
1693            // can't happen
1694        }
1695        return 0;
1696    }
1697
1698    private String printContextIdToJobMap(JobStatus[] map, String initial) {
1699        StringBuilder s = new StringBuilder(initial + ": ");
1700        for (int i=0; i<map.length; i++) {
1701            s.append("(")
1702                    .append(map[i] == null? -1: map[i].getJobId())
1703                    .append(map[i] == null? -1: map[i].getUid())
1704                    .append(")" );
1705        }
1706        return s.toString();
1707    }
1708
1709    private String printPendingQueue() {
1710        StringBuilder s = new StringBuilder("Pending queue: ");
1711        Iterator<JobStatus> it = mPendingJobs.iterator();
1712        while (it.hasNext()) {
1713            JobStatus js = it.next();
1714            s.append("(")
1715                    .append(js.getJob().getId())
1716                    .append(", ")
1717                    .append(js.getUid())
1718                    .append(") ");
1719        }
1720        return s.toString();
1721    }
1722
1723    static void dumpHelp(PrintWriter pw) {
1724        pw.println("Job Scheduler (jobscheduler) dump options:");
1725        pw.println("  [-h] [package] ...");
1726        pw.println("    -h: print this help");
1727        pw.println("  [package] is an optional package name to limit the output to.");
1728    }
1729
1730    void dumpInternal(final PrintWriter pw, String[] args) {
1731        int filterUid = -1;
1732        if (!ArrayUtils.isEmpty(args)) {
1733            int opti = 0;
1734            while (opti < args.length) {
1735                String arg = args[opti];
1736                if ("-h".equals(arg)) {
1737                    dumpHelp(pw);
1738                    return;
1739                } else if ("-a".equals(arg)) {
1740                    // Ignore, we always dump all.
1741                } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1742                    pw.println("Unknown option: " + arg);
1743                    return;
1744                } else {
1745                    break;
1746                }
1747                opti++;
1748            }
1749            if (opti < args.length) {
1750                String pkg = args[opti];
1751                try {
1752                    filterUid = getContext().getPackageManager().getPackageUid(pkg,
1753                            PackageManager.MATCH_UNINSTALLED_PACKAGES);
1754                } catch (NameNotFoundException ignored) {
1755                    pw.println("Invalid package: " + pkg);
1756                    return;
1757                }
1758            }
1759        }
1760
1761        final int filterUidFinal = UserHandle.getAppId(filterUid);
1762        final long now = SystemClock.elapsedRealtime();
1763        synchronized (mLock) {
1764            mConstants.dump(pw);
1765            pw.println();
1766            pw.println("Started users: " + Arrays.toString(mStartedUsers));
1767            pw.print("Registered ");
1768            pw.print(mJobs.size());
1769            pw.println(" jobs:");
1770            if (mJobs.size() > 0) {
1771                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1772                Collections.sort(jobs, new Comparator<JobStatus>() {
1773                    @Override
1774                    public int compare(JobStatus o1, JobStatus o2) {
1775                        int uid1 = o1.getUid();
1776                        int uid2 = o2.getUid();
1777                        int id1 = o1.getJobId();
1778                        int id2 = o2.getJobId();
1779                        if (uid1 != uid2) {
1780                            return uid1 < uid2 ? -1 : 1;
1781                        }
1782                        return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
1783                    }
1784                });
1785                for (JobStatus job : jobs) {
1786                    pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
1787                    pw.println(job.toShortStringExceptUniqueId());
1788
1789                    // Skip printing details if the caller requested a filter
1790                    if (!job.shouldDump(filterUidFinal)) {
1791                        continue;
1792                    }
1793
1794                    job.dump(pw, "    ", true);
1795                    pw.print("    Ready: ");
1796                    pw.print(mHandler.isReadyToBeExecutedLocked(job));
1797                    pw.print(" (job=");
1798                    pw.print(job.isReady());
1799                    pw.print(" pending=");
1800                    pw.print(mPendingJobs.contains(job));
1801                    pw.print(" active=");
1802                    pw.print(isCurrentlyActiveLocked(job));
1803                    pw.print(" user=");
1804                    pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
1805                    pw.println(")");
1806                }
1807            } else {
1808                pw.println("  None.");
1809            }
1810            for (int i=0; i<mControllers.size(); i++) {
1811                pw.println();
1812                mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
1813            }
1814            pw.println();
1815            pw.println("Uid priority overrides:");
1816            for (int i=0; i< mUidPriorityOverride.size(); i++) {
1817                int uid = mUidPriorityOverride.keyAt(i);
1818                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
1819                    pw.print("  "); pw.print(UserHandle.formatUid(uid));
1820                    pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
1821                }
1822            }
1823            pw.println();
1824            mJobPackageTracker.dump(pw, "", filterUidFinal);
1825            pw.println();
1826            if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
1827                pw.println();
1828            }
1829            pw.println("Pending queue:");
1830            for (int i=0; i<mPendingJobs.size(); i++) {
1831                JobStatus job = mPendingJobs.get(i);
1832                pw.print("  Pending #"); pw.print(i); pw.print(": ");
1833                pw.println(job.toShortString());
1834                job.dump(pw, "    ", false);
1835                int priority = evaluateJobPriorityLocked(job);
1836                if (priority != JobInfo.PRIORITY_DEFAULT) {
1837                    pw.print("    Evaluated priority: "); pw.println(priority);
1838                }
1839                pw.print("    Tag: "); pw.println(job.getTag());
1840            }
1841            pw.println();
1842            pw.println("Active jobs:");
1843            for (int i=0; i<mActiveServices.size(); i++) {
1844                JobServiceContext jsc = mActiveServices.get(i);
1845                pw.print("  Slot #"); pw.print(i); pw.print(": ");
1846                if (jsc.getRunningJob() == null) {
1847                    pw.println("inactive");
1848                    continue;
1849                } else {
1850                    pw.println(jsc.getRunningJob().toShortString());
1851                    pw.print("    Running for: ");
1852                    TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
1853                    pw.print(", timeout at: ");
1854                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
1855                    pw.println();
1856                    jsc.getRunningJob().dump(pw, "    ", false);
1857                    int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
1858                    if (priority != JobInfo.PRIORITY_DEFAULT) {
1859                        pw.print("    Evaluated priority: "); pw.println(priority);
1860                    }
1861                }
1862            }
1863            if (filterUid == -1) {
1864                pw.println();
1865                pw.print("mReadyToRock="); pw.println(mReadyToRock);
1866                pw.print("mReportedActive="); pw.println(mReportedActive);
1867                pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
1868            }
1869        }
1870        pw.println();
1871    }
1872}
1873