JobSchedulerService.java revision 34618b586996dfbfd87a3b7f0d18f26d4d1ebb51
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                // except those using the idle exemption flag.
667                for (int i=0; i<mActiveServices.size(); i++) {
668                    JobServiceContext jsc = mActiveServices.get(i);
669                    final JobStatus executing = jsc.getRunningJob();
670                    if (executing != null
671                            && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
672                        jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
673                    }
674                }
675            } else {
676                // When coming out of idle, allow thing to start back up.
677                if (mReadyToRock) {
678                    if (mLocalDeviceIdleController != null) {
679                        if (!mReportedActive) {
680                            mReportedActive = true;
681                            mLocalDeviceIdleController.setJobsActive(true);
682                        }
683                    }
684                }
685                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
686            }
687        }
688    }
689
690    void reportActive() {
691        // active is true if pending queue contains jobs OR some job is running.
692        boolean active = mPendingJobs.size() > 0;
693        if (mPendingJobs.size() <= 0) {
694            for (int i=0; i<mActiveServices.size(); i++) {
695                JobServiceContext jsc = mActiveServices.get(i);
696                if (jsc.getRunningJob() != null) {
697                    active = true;
698                    break;
699                }
700            }
701        }
702
703        if (mReportedActive != active) {
704            mReportedActive = active;
705            if (mLocalDeviceIdleController != null) {
706                mLocalDeviceIdleController.setJobsActive(active);
707            }
708        }
709    }
710
711    /**
712     * Initializes the system service.
713     * <p>
714     * Subclasses must define a single argument constructor that accepts the context
715     * and passes it to super.
716     * </p>
717     *
718     * @param context The system server context.
719     */
720    public JobSchedulerService(Context context) {
721        super(context);
722        mHandler = new JobHandler(context.getMainLooper());
723        mConstants = new Constants(mHandler);
724        mJobSchedulerStub = new JobSchedulerStub();
725        mJobs = JobStore.initAndGet(this);
726
727        // Create the controllers.
728        mControllers = new ArrayList<StateController>();
729        mControllers.add(ConnectivityController.get(this));
730        mControllers.add(TimeController.get(this));
731        mControllers.add(IdleController.get(this));
732        mControllers.add(BatteryController.get(this));
733        mControllers.add(AppIdleController.get(this));
734        mControllers.add(ContentObserverController.get(this));
735        mControllers.add(DeviceIdleJobsController.get(this));
736    }
737
738    @Override
739    public void onStart() {
740        publishLocalService(JobSchedulerInternal.class, new LocalService());
741        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
742    }
743
744    @Override
745    public void onBootPhase(int phase) {
746        if (PHASE_SYSTEM_SERVICES_READY == phase) {
747            mConstants.start(getContext().getContentResolver());
748            // Register br for package removals and user removals.
749            final IntentFilter filter = new IntentFilter();
750            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
751            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
752            filter.addDataScheme("package");
753            getContext().registerReceiverAsUser(
754                    mBroadcastReceiver, UserHandle.ALL, filter, null, null);
755            final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
756            getContext().registerReceiverAsUser(
757                    mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
758            mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
759            try {
760                ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
761                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
762                        | ActivityManager.UID_OBSERVER_IDLE);
763            } catch (RemoteException e) {
764                // ignored; both services live in system_server
765            }
766        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
767            synchronized (mLock) {
768                // Let's go!
769                mReadyToRock = true;
770                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
771                        BatteryStats.SERVICE_NAME));
772                mLocalDeviceIdleController
773                        = LocalServices.getService(DeviceIdleController.LocalService.class);
774                // Create the "runners".
775                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
776                    mActiveServices.add(
777                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
778                                    getContext().getMainLooper()));
779                }
780                // Attach jobs to their controllers.
781                mJobs.forEachJob(new JobStatusFunctor() {
782                    @Override
783                    public void process(JobStatus job) {
784                        for (int controller = 0; controller < mControllers.size(); controller++) {
785                            final StateController sc = mControllers.get(controller);
786                            sc.maybeStartTrackingJobLocked(job, null);
787                        }
788                    }
789                });
790                // GO GO GO!
791                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
792            }
793        }
794    }
795
796    /**
797     * Called when we have a job status object that we need to insert in our
798     * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
799     * about.
800     */
801    private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
802        synchronized (mLock) {
803            final boolean update = mJobs.add(jobStatus);
804            if (mReadyToRock) {
805                for (int i = 0; i < mControllers.size(); i++) {
806                    StateController controller = mControllers.get(i);
807                    if (update) {
808                        controller.maybeStopTrackingJobLocked(jobStatus, null, true);
809                    }
810                    controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
811                }
812            }
813        }
814    }
815
816    /**
817     * Called when we want to remove a JobStatus object that we've finished executing. Returns the
818     * object removed.
819     */
820    private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
821            boolean writeBack) {
822        synchronized (mLock) {
823            // Remove from store as well as controllers.
824            final boolean removed = mJobs.remove(jobStatus, writeBack);
825            if (removed && mReadyToRock) {
826                for (int i=0; i<mControllers.size(); i++) {
827                    StateController controller = mControllers.get(i);
828                    controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
829                }
830            }
831            return removed;
832        }
833    }
834
835    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
836        for (int i=0; i<mActiveServices.size(); i++) {
837            JobServiceContext jsc = mActiveServices.get(i);
838            final JobStatus executing = jsc.getRunningJob();
839            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
840                jsc.cancelExecutingJob(reason);
841                return true;
842            }
843        }
844        return false;
845    }
846
847    /**
848     * @param job JobStatus we are querying against.
849     * @return Whether or not the job represented by the status object is currently being run or
850     * is pending.
851     */
852    private boolean isCurrentlyActiveLocked(JobStatus job) {
853        for (int i=0; i<mActiveServices.size(); i++) {
854            JobServiceContext serviceContext = mActiveServices.get(i);
855            final JobStatus running = serviceContext.getRunningJob();
856            if (running != null && running.matches(job.getUid(), job.getJobId())) {
857                return true;
858            }
859        }
860        return false;
861    }
862
863    void noteJobsPending(List<JobStatus> jobs) {
864        for (int i = jobs.size() - 1; i >= 0; i--) {
865            JobStatus job = jobs.get(i);
866            mJobPackageTracker.notePending(job);
867        }
868    }
869
870    void noteJobsNonpending(List<JobStatus> jobs) {
871        for (int i = jobs.size() - 1; i >= 0; i--) {
872            JobStatus job = jobs.get(i);
873            mJobPackageTracker.noteNonpending(job);
874        }
875    }
876
877    /**
878     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
879     * specify an override deadline on a failed job (the failed job will run even though it's not
880     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
881     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
882     *
883     * @param failureToReschedule Provided job status that we will reschedule.
884     * @return A newly instantiated JobStatus with the same constraints as the last job except
885     * with adjusted timing constraints.
886     *
887     * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
888     */
889    private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
890        final long elapsedNowMillis = SystemClock.elapsedRealtime();
891        final JobInfo job = failureToReschedule.getJob();
892
893        final long initialBackoffMillis = job.getInitialBackoffMillis();
894        final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
895        long delayMillis;
896
897        switch (job.getBackoffPolicy()) {
898            case JobInfo.BACKOFF_POLICY_LINEAR:
899                delayMillis = initialBackoffMillis * backoffAttempts;
900                break;
901            default:
902                if (DEBUG) {
903                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
904                }
905            case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
906                delayMillis =
907                        (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
908                break;
909        }
910        delayMillis =
911                Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
912        JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
913                JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
914        for (int ic=0; ic<mControllers.size(); ic++) {
915            StateController controller = mControllers.get(ic);
916            controller.rescheduleForFailure(newJob, failureToReschedule);
917        }
918        return newJob;
919    }
920
921    /**
922     * Called after a periodic has executed so we can reschedule it. We take the last execution
923     * time of the job to be the time of completion (i.e. the time at which this function is
924     * called).
925     * This could be inaccurate b/c the job can run for as long as
926     * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
927     * to underscheduling at least, rather than if we had taken the last execution time to be the
928     * start of the execution.
929     * @return A new job representing the execution criteria for this instantiation of the
930     * recurring job.
931     */
932    private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
933        final long elapsedNow = SystemClock.elapsedRealtime();
934        // Compute how much of the period is remaining.
935        long runEarly = 0L;
936
937        // If this periodic was rescheduled it won't have a deadline.
938        if (periodicToReschedule.hasDeadlineConstraint()) {
939            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
940        }
941        long flex = periodicToReschedule.getJob().getFlexMillis();
942        long period = periodicToReschedule.getJob().getIntervalMillis();
943        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
944        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
945
946        if (DEBUG) {
947            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
948                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
949        }
950        return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
951                newLatestRuntimeElapsed, 0 /* backoffAttempt */);
952    }
953
954    // JobCompletedListener implementations.
955
956    /**
957     * A job just finished executing. We fetch the
958     * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
959     * whether we want to reschedule we readd it to the controllers.
960     * @param jobStatus Completed job.
961     * @param needsReschedule Whether the implementing class should reschedule this job.
962     */
963    @Override
964    public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
965        if (DEBUG) {
966            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
967        }
968        // Do not write back immediately if this is a periodic job. The job may get lost if system
969        // shuts down before it is added back.
970        if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
971            if (DEBUG) {
972                Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
973            }
974            // We still want to check for jobs to execute, because this job may have
975            // scheduled a new job under the same job id, and now we can run it.
976            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
977            return;
978        }
979        // Note: there is a small window of time in here where, when rescheduling a job,
980        // we will stop monitoring its content providers.  This should be fixed by stopping
981        // the old job after scheduling the new one, but since we have no lock held here
982        // that may cause ordering problems if the app removes jobStatus while in here.
983        if (needsReschedule) {
984            JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
985            startTrackingJob(rescheduled, jobStatus);
986        } else if (jobStatus.getJob().isPeriodic()) {
987            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
988            startTrackingJob(rescheduledPeriodic, jobStatus);
989        }
990        reportActive();
991        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
992    }
993
994    // StateChangedListener implementations.
995
996    /**
997     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
998     * some controller's state has changed, so as to run through the list of jobs and start/stop
999     * any that are eligible.
1000     */
1001    @Override
1002    public void onControllerStateChanged() {
1003        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1004    }
1005
1006    @Override
1007    public void onRunJobNow(JobStatus jobStatus) {
1008        mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1009    }
1010
1011    private class JobHandler extends Handler {
1012
1013        public JobHandler(Looper looper) {
1014            super(looper);
1015        }
1016
1017        @Override
1018        public void handleMessage(Message message) {
1019            synchronized (mLock) {
1020                if (!mReadyToRock) {
1021                    return;
1022                }
1023            }
1024            switch (message.what) {
1025                case MSG_JOB_EXPIRED:
1026                    synchronized (mLock) {
1027                        JobStatus runNow = (JobStatus) message.obj;
1028                        // runNow can be null, which is a controller's way of indicating that its
1029                        // state is such that all ready jobs should be run immediately.
1030                        if (runNow != null && !mPendingJobs.contains(runNow)
1031                                && mJobs.containsJob(runNow)) {
1032                            mJobPackageTracker.notePending(runNow);
1033                            mPendingJobs.add(runNow);
1034                        }
1035                        queueReadyJobsForExecutionLockedH();
1036                    }
1037                    break;
1038                case MSG_CHECK_JOB:
1039                    synchronized (mLock) {
1040                        if (mReportedActive) {
1041                            // if jobs are currently being run, queue all ready jobs for execution.
1042                            queueReadyJobsForExecutionLockedH();
1043                        } else {
1044                            // Check the list of jobs and run some of them if we feel inclined.
1045                            maybeQueueReadyJobsForExecutionLockedH();
1046                        }
1047                    }
1048                    break;
1049                case MSG_CHECK_JOB_GREEDY:
1050                    synchronized (mLock) {
1051                        queueReadyJobsForExecutionLockedH();
1052                    }
1053                    break;
1054                case MSG_STOP_JOB:
1055                    cancelJobImpl((JobStatus)message.obj, null);
1056                    break;
1057            }
1058            maybeRunPendingJobsH();
1059            // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1060            removeMessages(MSG_CHECK_JOB);
1061        }
1062
1063        /**
1064         * Run through list of jobs and execute all possible - at least one is expired so we do
1065         * as many as we can.
1066         */
1067        private void queueReadyJobsForExecutionLockedH() {
1068            if (DEBUG) {
1069                Slog.d(TAG, "queuing all ready jobs for execution:");
1070            }
1071            noteJobsNonpending(mPendingJobs);
1072            mPendingJobs.clear();
1073            mJobs.forEachJob(mReadyQueueFunctor);
1074            mReadyQueueFunctor.postProcess();
1075
1076            if (DEBUG) {
1077                final int queuedJobs = mPendingJobs.size();
1078                if (queuedJobs == 0) {
1079                    Slog.d(TAG, "No jobs pending.");
1080                } else {
1081                    Slog.d(TAG, queuedJobs + " jobs queued.");
1082                }
1083            }
1084        }
1085
1086        class ReadyJobQueueFunctor implements JobStatusFunctor {
1087            ArrayList<JobStatus> newReadyJobs;
1088
1089            @Override
1090            public void process(JobStatus job) {
1091                if (isReadyToBeExecutedLocked(job)) {
1092                    if (DEBUG) {
1093                        Slog.d(TAG, "    queued " + job.toShortString());
1094                    }
1095                    if (newReadyJobs == null) {
1096                        newReadyJobs = new ArrayList<JobStatus>();
1097                    }
1098                    newReadyJobs.add(job);
1099                } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1100                    stopJobOnServiceContextLocked(job,
1101                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1102                }
1103            }
1104
1105            public void postProcess() {
1106                if (newReadyJobs != null) {
1107                    noteJobsPending(newReadyJobs);
1108                    mPendingJobs.addAll(newReadyJobs);
1109                }
1110                newReadyJobs = null;
1111            }
1112        }
1113        private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1114
1115        /**
1116         * The state of at least one job has changed. Here is where we could enforce various
1117         * policies on when we want to execute jobs.
1118         * Right now the policy is such:
1119         * If >1 of the ready jobs is idle mode we send all of them off
1120         * if more than 2 network connectivity jobs are ready we send them all off.
1121         * If more than 4 jobs total are ready we send them all off.
1122         * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1123         */
1124        class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1125            int chargingCount;
1126            int idleCount;
1127            int backoffCount;
1128            int connectivityCount;
1129            int contentCount;
1130            List<JobStatus> runnableJobs;
1131
1132            public MaybeReadyJobQueueFunctor() {
1133                reset();
1134            }
1135
1136            // Functor method invoked for each job via JobStore.forEachJob()
1137            @Override
1138            public void process(JobStatus job) {
1139                if (isReadyToBeExecutedLocked(job)) {
1140                    try {
1141                        if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
1142                                job.getJob().getService().getPackageName())
1143                                == ActivityManager.APP_START_MODE_DISABLED) {
1144                            Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1145                                    + job.getJob().toString() + " -- package not allowed to start");
1146                            mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1147                            return;
1148                        }
1149                    } catch (RemoteException e) {
1150                    }
1151                    if (job.getNumFailures() > 0) {
1152                        backoffCount++;
1153                    }
1154                    if (job.hasIdleConstraint()) {
1155                        idleCount++;
1156                    }
1157                    if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1158                            || job.hasNotRoamingConstraint()) {
1159                        connectivityCount++;
1160                    }
1161                    if (job.hasChargingConstraint()) {
1162                        chargingCount++;
1163                    }
1164                    if (job.hasContentTriggerConstraint()) {
1165                        contentCount++;
1166                    }
1167                    if (runnableJobs == null) {
1168                        runnableJobs = new ArrayList<>();
1169                    }
1170                    runnableJobs.add(job);
1171                } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1172                    stopJobOnServiceContextLocked(job,
1173                            JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1174                }
1175            }
1176
1177            public void postProcess() {
1178                if (backoffCount > 0 ||
1179                        idleCount >= mConstants.MIN_IDLE_COUNT ||
1180                        connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1181                        chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1182                        contentCount >= mConstants.MIN_CONTENT_COUNT ||
1183                        (runnableJobs != null
1184                                && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1185                    if (DEBUG) {
1186                        Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1187                    }
1188                    noteJobsPending(runnableJobs);
1189                    mPendingJobs.addAll(runnableJobs);
1190                } else {
1191                    if (DEBUG) {
1192                        Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1193                    }
1194                }
1195
1196                // Be ready for next time
1197                reset();
1198            }
1199
1200            private void reset() {
1201                chargingCount = 0;
1202                idleCount =  0;
1203                backoffCount = 0;
1204                connectivityCount = 0;
1205                contentCount = 0;
1206                runnableJobs = null;
1207            }
1208        }
1209        private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1210
1211        private void maybeQueueReadyJobsForExecutionLockedH() {
1212            if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1213
1214            noteJobsNonpending(mPendingJobs);
1215            mPendingJobs.clear();
1216            mJobs.forEachJob(mMaybeQueueFunctor);
1217            mMaybeQueueFunctor.postProcess();
1218        }
1219
1220        /**
1221         * Criteria for moving a job into the pending queue:
1222         *      - It's ready.
1223         *      - It's not pending.
1224         *      - It's not already running on a JSC.
1225         *      - The user that requested the job is running.
1226         *      - The component is enabled and runnable.
1227         */
1228        private boolean isReadyToBeExecutedLocked(JobStatus job) {
1229            final boolean jobReady = job.isReady();
1230            final boolean jobPending = mPendingJobs.contains(job);
1231            final boolean jobActive = isCurrentlyActiveLocked(job);
1232
1233            final int userId = job.getUserId();
1234            final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1235            final boolean componentPresent;
1236            try {
1237                componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1238                        job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1239                        userId) != null);
1240            } catch (RemoteException e) {
1241                throw e.rethrowAsRuntimeException();
1242            }
1243
1244            if (DEBUG) {
1245                Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1246                        + " ready=" + jobReady + " pending=" + jobPending
1247                        + " active=" + jobActive + " userStarted=" + userStarted
1248                        + " componentPresent=" + componentPresent);
1249            }
1250            return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
1251        }
1252
1253        /**
1254         * Criteria for cancelling an active job:
1255         *      - It's not ready
1256         *      - It's running on a JSC.
1257         */
1258        private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
1259            return !job.isReady() && isCurrentlyActiveLocked(job);
1260        }
1261
1262        /**
1263         * Reconcile jobs in the pending queue against available execution contexts.
1264         * A controller can force a job into the pending queue even if it's already running, but
1265         * here is where we decide whether to actually execute it.
1266         */
1267        private void maybeRunPendingJobsH() {
1268            synchronized (mLock) {
1269                if (DEBUG) {
1270                    Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1271                }
1272                assignJobsToContextsLocked();
1273                reportActive();
1274            }
1275        }
1276    }
1277
1278    private int adjustJobPriority(int curPriority, JobStatus job) {
1279        if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1280            float factor = mJobPackageTracker.getLoadFactor(job);
1281            if (factor >= mConstants.HEAVY_USE_FACTOR) {
1282                curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
1283            } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
1284                curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1285            }
1286        }
1287        return curPriority;
1288    }
1289
1290    private int evaluateJobPriorityLocked(JobStatus job) {
1291        int priority = job.getPriority();
1292        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1293            return adjustJobPriority(priority, job);
1294        }
1295        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1296        if (override != 0) {
1297            return adjustJobPriority(override, job);
1298        }
1299        return adjustJobPriority(priority, job);
1300    }
1301
1302    /**
1303     * Takes jobs from pending queue and runs them on available contexts.
1304     * If no contexts are available, preempts lower priority jobs to
1305     * run higher priority ones.
1306     * Lock on mJobs before calling this function.
1307     */
1308    private void assignJobsToContextsLocked() {
1309        if (DEBUG) {
1310            Slog.d(TAG, printPendingQueue());
1311        }
1312
1313        int memLevel;
1314        try {
1315            memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
1316        } catch (RemoteException e) {
1317            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1318        }
1319        switch (memLevel) {
1320            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1321                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1322                break;
1323            case ProcessStats.ADJ_MEM_FACTOR_LOW:
1324                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1325                break;
1326            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1327                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1328                break;
1329            default:
1330                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1331                break;
1332        }
1333
1334        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1335        boolean[] act = mTmpAssignAct;
1336        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1337        int numActive = 0;
1338        int numForeground = 0;
1339        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1340            final JobServiceContext js = mActiveServices.get(i);
1341            final JobStatus status = js.getRunningJob();
1342            if ((contextIdToJobMap[i] = status) != null) {
1343                numActive++;
1344                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1345                    numForeground++;
1346                }
1347            }
1348            act[i] = false;
1349            preferredUidForContext[i] = js.getPreferredUid();
1350        }
1351        if (DEBUG) {
1352            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1353        }
1354        for (int i=0; i<mPendingJobs.size(); i++) {
1355            JobStatus nextPending = mPendingJobs.get(i);
1356
1357            // If job is already running, go to next job.
1358            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1359            if (jobRunningContext != -1) {
1360                continue;
1361            }
1362
1363            final int priority = evaluateJobPriorityLocked(nextPending);
1364            nextPending.lastEvaluatedPriority = priority;
1365
1366            // Find a context for nextPending. The context should be available OR
1367            // it should have lowest priority among all running jobs
1368            // (sharing the same Uid as nextPending)
1369            int minPriority = Integer.MAX_VALUE;
1370            int minPriorityContextId = -1;
1371            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1372                JobStatus job = contextIdToJobMap[j];
1373                int preferredUid = preferredUidForContext[j];
1374                if (job == null) {
1375                    if ((numActive < mMaxActiveJobs ||
1376                            (priority >= JobInfo.PRIORITY_TOP_APP &&
1377                                    numForeground < mConstants.FG_JOB_COUNT)) &&
1378                            (preferredUid == nextPending.getUid() ||
1379                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1380                        // This slot is free, and we haven't yet hit the limit on
1381                        // concurrent jobs...  we can just throw the job in to here.
1382                        minPriorityContextId = j;
1383                        break;
1384                    }
1385                    // No job on this context, but nextPending can't run here because
1386                    // the context has a preferred Uid or we have reached the limit on
1387                    // concurrent jobs.
1388                    continue;
1389                }
1390                if (job.getUid() != nextPending.getUid()) {
1391                    continue;
1392                }
1393                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1394                    continue;
1395                }
1396                if (minPriority > nextPending.lastEvaluatedPriority) {
1397                    minPriority = nextPending.lastEvaluatedPriority;
1398                    minPriorityContextId = j;
1399                }
1400            }
1401            if (minPriorityContextId != -1) {
1402                contextIdToJobMap[minPriorityContextId] = nextPending;
1403                act[minPriorityContextId] = true;
1404                numActive++;
1405                if (priority >= JobInfo.PRIORITY_TOP_APP) {
1406                    numForeground++;
1407                }
1408            }
1409        }
1410        if (DEBUG) {
1411            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1412        }
1413        mJobPackageTracker.noteConcurrency(numActive, numForeground);
1414        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1415            boolean preservePreferredUid = false;
1416            if (act[i]) {
1417                JobStatus js = mActiveServices.get(i).getRunningJob();
1418                if (js != null) {
1419                    if (DEBUG) {
1420                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1421                    }
1422                    // preferredUid will be set to uid of currently running job.
1423                    mActiveServices.get(i).preemptExecutingJob();
1424                    preservePreferredUid = true;
1425                } else {
1426                    final JobStatus pendingJob = contextIdToJobMap[i];
1427                    if (DEBUG) {
1428                        Slog.d(TAG, "About to run job on context "
1429                                + String.valueOf(i) + ", job: " + pendingJob);
1430                    }
1431                    for (int ic=0; ic<mControllers.size(); ic++) {
1432                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1433                    }
1434                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1435                        Slog.d(TAG, "Error executing " + pendingJob);
1436                    }
1437                    if (mPendingJobs.remove(pendingJob)) {
1438                        mJobPackageTracker.noteNonpending(pendingJob);
1439                    }
1440                }
1441            }
1442            if (!preservePreferredUid) {
1443                mActiveServices.get(i).clearPreferredUid();
1444            }
1445        }
1446    }
1447
1448    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1449        for (int i=0; i<map.length; i++) {
1450            if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1451                return i;
1452            }
1453        }
1454        return -1;
1455    }
1456
1457    final class LocalService implements JobSchedulerInternal {
1458
1459        /**
1460         * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1461         * jobs are always considered pending.
1462         */
1463        @Override
1464        public List<JobInfo> getSystemScheduledPendingJobs() {
1465            synchronized (mLock) {
1466                final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1467                mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1468                    @Override
1469                    public void process(JobStatus job) {
1470                        if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1471                            pendingJobs.add(job.getJob());
1472                        }
1473                    }
1474                });
1475                return pendingJobs;
1476            }
1477        }
1478    }
1479
1480    /**
1481     * Binder stub trampoline implementation
1482     */
1483    final class JobSchedulerStub extends IJobScheduler.Stub {
1484        /** Cache determination of whether a given app can persist jobs
1485         * key is uid of the calling app; value is undetermined/true/false
1486         */
1487        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1488
1489        // Enforce that only the app itself (or shared uid participant) can schedule a
1490        // job that runs one of the app's services, as well as verifying that the
1491        // named service properly requires the BIND_JOB_SERVICE permission
1492        private void enforceValidJobRequest(int uid, JobInfo job) {
1493            final IPackageManager pm = AppGlobals.getPackageManager();
1494            final ComponentName service = job.getService();
1495            try {
1496                ServiceInfo si = pm.getServiceInfo(service,
1497                        PackageManager.MATCH_DIRECT_BOOT_AWARE
1498                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1499                        UserHandle.getUserId(uid));
1500                if (si == null) {
1501                    throw new IllegalArgumentException("No such service " + service);
1502                }
1503                if (si.applicationInfo.uid != uid) {
1504                    throw new IllegalArgumentException("uid " + uid +
1505                            " cannot schedule job in " + service.getPackageName());
1506                }
1507                if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1508                    throw new IllegalArgumentException("Scheduled service " + service
1509                            + " does not require android.permission.BIND_JOB_SERVICE permission");
1510                }
1511            } catch (RemoteException e) {
1512                // Can't happen; the Package Manager is in this same process
1513            }
1514        }
1515
1516        private boolean canPersistJobs(int pid, int uid) {
1517            // If we get this far we're good to go; all we need to do now is check
1518            // whether the app is allowed to persist its scheduled work.
1519            final boolean canPersist;
1520            synchronized (mPersistCache) {
1521                Boolean cached = mPersistCache.get(uid);
1522                if (cached != null) {
1523                    canPersist = cached.booleanValue();
1524                } else {
1525                    // Persisting jobs is tantamount to running at boot, so we permit
1526                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1527                    // permission
1528                    int result = getContext().checkPermission(
1529                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1530                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
1531                    mPersistCache.put(uid, canPersist);
1532                }
1533            }
1534            return canPersist;
1535        }
1536
1537        // IJobScheduler implementation
1538        @Override
1539        public int schedule(JobInfo job) throws RemoteException {
1540            if (DEBUG) {
1541                Slog.d(TAG, "Scheduling job: " + job.toString());
1542            }
1543            final int pid = Binder.getCallingPid();
1544            final int uid = Binder.getCallingUid();
1545
1546            enforceValidJobRequest(uid, job);
1547            if (job.isPersisted()) {
1548                if (!canPersistJobs(pid, uid)) {
1549                    throw new IllegalArgumentException("Error: requested job be persisted without"
1550                            + " holding RECEIVE_BOOT_COMPLETED permission.");
1551                }
1552            }
1553
1554            long ident = Binder.clearCallingIdentity();
1555            try {
1556                return JobSchedulerService.this.schedule(job, uid);
1557            } finally {
1558                Binder.restoreCallingIdentity(ident);
1559            }
1560        }
1561
1562        @Override
1563        public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1564                throws RemoteException {
1565            final int callerUid = Binder.getCallingUid();
1566            if (DEBUG) {
1567                Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1568                        + " on behalf of " + packageName);
1569            }
1570
1571            if (packageName == null) {
1572                throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1573            }
1574
1575            int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1576                    android.Manifest.permission.UPDATE_DEVICE_STATS);
1577            if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1578                throw new SecurityException("Caller uid " + callerUid
1579                        + " not permitted to schedule jobs for other apps");
1580            }
1581
1582            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1583                getContext().enforceCallingOrSelfPermission(
1584                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1585            }
1586
1587            long ident = Binder.clearCallingIdentity();
1588            try {
1589                return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
1590                        packageName, userId, tag);
1591            } finally {
1592                Binder.restoreCallingIdentity(ident);
1593            }
1594        }
1595
1596        @Override
1597        public List<JobInfo> getAllPendingJobs() throws RemoteException {
1598            final int uid = Binder.getCallingUid();
1599
1600            long ident = Binder.clearCallingIdentity();
1601            try {
1602                return JobSchedulerService.this.getPendingJobs(uid);
1603            } finally {
1604                Binder.restoreCallingIdentity(ident);
1605            }
1606        }
1607
1608        @Override
1609        public JobInfo getPendingJob(int jobId) throws RemoteException {
1610            final int uid = Binder.getCallingUid();
1611
1612            long ident = Binder.clearCallingIdentity();
1613            try {
1614                return JobSchedulerService.this.getPendingJob(uid, jobId);
1615            } finally {
1616                Binder.restoreCallingIdentity(ident);
1617            }
1618        }
1619
1620        @Override
1621        public void cancelAll() throws RemoteException {
1622            final int uid = Binder.getCallingUid();
1623
1624            long ident = Binder.clearCallingIdentity();
1625            try {
1626                JobSchedulerService.this.cancelJobsForUid(uid, true);
1627            } finally {
1628                Binder.restoreCallingIdentity(ident);
1629            }
1630        }
1631
1632        @Override
1633        public void cancel(int jobId) throws RemoteException {
1634            final int uid = Binder.getCallingUid();
1635
1636            long ident = Binder.clearCallingIdentity();
1637            try {
1638                JobSchedulerService.this.cancelJob(uid, jobId);
1639            } finally {
1640                Binder.restoreCallingIdentity(ident);
1641            }
1642        }
1643
1644        /**
1645         * "dumpsys" infrastructure
1646         */
1647        @Override
1648        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1649            getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1650
1651            long identityToken = Binder.clearCallingIdentity();
1652            try {
1653                JobSchedulerService.this.dumpInternal(pw, args);
1654            } finally {
1655                Binder.restoreCallingIdentity(identityToken);
1656            }
1657        }
1658
1659        @Override
1660        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1661                String[] args, ResultReceiver resultReceiver) throws RemoteException {
1662                (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
1663                        this, in, out, err, args, resultReceiver);
1664        }
1665    };
1666
1667    // Shell command infrastructure: run the given job immediately
1668    int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1669        if (DEBUG) {
1670            Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1671                    + " " + jobId + " f=" + force);
1672        }
1673
1674        try {
1675            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1676            if (uid < 0) {
1677                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1678            }
1679
1680            synchronized (mLock) {
1681                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1682                if (js == null) {
1683                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1684                }
1685
1686                js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1687                if (!js.isConstraintsSatisfied()) {
1688                    js.overrideState = 0;
1689                    return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1690                }
1691
1692                mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1693            }
1694        } catch (RemoteException e) {
1695            // can't happen
1696        }
1697        return 0;
1698    }
1699
1700    private String printContextIdToJobMap(JobStatus[] map, String initial) {
1701        StringBuilder s = new StringBuilder(initial + ": ");
1702        for (int i=0; i<map.length; i++) {
1703            s.append("(")
1704                    .append(map[i] == null? -1: map[i].getJobId())
1705                    .append(map[i] == null? -1: map[i].getUid())
1706                    .append(")" );
1707        }
1708        return s.toString();
1709    }
1710
1711    private String printPendingQueue() {
1712        StringBuilder s = new StringBuilder("Pending queue: ");
1713        Iterator<JobStatus> it = mPendingJobs.iterator();
1714        while (it.hasNext()) {
1715            JobStatus js = it.next();
1716            s.append("(")
1717                    .append(js.getJob().getId())
1718                    .append(", ")
1719                    .append(js.getUid())
1720                    .append(") ");
1721        }
1722        return s.toString();
1723    }
1724
1725    static void dumpHelp(PrintWriter pw) {
1726        pw.println("Job Scheduler (jobscheduler) dump options:");
1727        pw.println("  [-h] [package] ...");
1728        pw.println("    -h: print this help");
1729        pw.println("  [package] is an optional package name to limit the output to.");
1730    }
1731
1732    void dumpInternal(final PrintWriter pw, String[] args) {
1733        int filterUid = -1;
1734        if (!ArrayUtils.isEmpty(args)) {
1735            int opti = 0;
1736            while (opti < args.length) {
1737                String arg = args[opti];
1738                if ("-h".equals(arg)) {
1739                    dumpHelp(pw);
1740                    return;
1741                } else if ("-a".equals(arg)) {
1742                    // Ignore, we always dump all.
1743                } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1744                    pw.println("Unknown option: " + arg);
1745                    return;
1746                } else {
1747                    break;
1748                }
1749                opti++;
1750            }
1751            if (opti < args.length) {
1752                String pkg = args[opti];
1753                try {
1754                    filterUid = getContext().getPackageManager().getPackageUid(pkg,
1755                            PackageManager.MATCH_UNINSTALLED_PACKAGES);
1756                } catch (NameNotFoundException ignored) {
1757                    pw.println("Invalid package: " + pkg);
1758                    return;
1759                }
1760            }
1761        }
1762
1763        final int filterUidFinal = UserHandle.getAppId(filterUid);
1764        final long now = SystemClock.elapsedRealtime();
1765        synchronized (mLock) {
1766            mConstants.dump(pw);
1767            pw.println();
1768            pw.println("Started users: " + Arrays.toString(mStartedUsers));
1769            pw.print("Registered ");
1770            pw.print(mJobs.size());
1771            pw.println(" jobs:");
1772            if (mJobs.size() > 0) {
1773                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1774                Collections.sort(jobs, new Comparator<JobStatus>() {
1775                    @Override
1776                    public int compare(JobStatus o1, JobStatus o2) {
1777                        int uid1 = o1.getUid();
1778                        int uid2 = o2.getUid();
1779                        int id1 = o1.getJobId();
1780                        int id2 = o2.getJobId();
1781                        if (uid1 != uid2) {
1782                            return uid1 < uid2 ? -1 : 1;
1783                        }
1784                        return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
1785                    }
1786                });
1787                for (JobStatus job : jobs) {
1788                    pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
1789                    pw.println(job.toShortStringExceptUniqueId());
1790
1791                    // Skip printing details if the caller requested a filter
1792                    if (!job.shouldDump(filterUidFinal)) {
1793                        continue;
1794                    }
1795
1796                    job.dump(pw, "    ", true);
1797                    pw.print("    Ready: ");
1798                    pw.print(mHandler.isReadyToBeExecutedLocked(job));
1799                    pw.print(" (job=");
1800                    pw.print(job.isReady());
1801                    pw.print(" pending=");
1802                    pw.print(mPendingJobs.contains(job));
1803                    pw.print(" active=");
1804                    pw.print(isCurrentlyActiveLocked(job));
1805                    pw.print(" user=");
1806                    pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
1807                    pw.println(")");
1808                }
1809            } else {
1810                pw.println("  None.");
1811            }
1812            for (int i=0; i<mControllers.size(); i++) {
1813                pw.println();
1814                mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
1815            }
1816            pw.println();
1817            pw.println("Uid priority overrides:");
1818            for (int i=0; i< mUidPriorityOverride.size(); i++) {
1819                int uid = mUidPriorityOverride.keyAt(i);
1820                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
1821                    pw.print("  "); pw.print(UserHandle.formatUid(uid));
1822                    pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
1823                }
1824            }
1825            pw.println();
1826            mJobPackageTracker.dump(pw, "", filterUidFinal);
1827            pw.println();
1828            if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
1829                pw.println();
1830            }
1831            pw.println("Pending queue:");
1832            for (int i=0; i<mPendingJobs.size(); i++) {
1833                JobStatus job = mPendingJobs.get(i);
1834                pw.print("  Pending #"); pw.print(i); pw.print(": ");
1835                pw.println(job.toShortString());
1836                job.dump(pw, "    ", false);
1837                int priority = evaluateJobPriorityLocked(job);
1838                if (priority != JobInfo.PRIORITY_DEFAULT) {
1839                    pw.print("    Evaluated priority: "); pw.println(priority);
1840                }
1841                pw.print("    Tag: "); pw.println(job.getTag());
1842            }
1843            pw.println();
1844            pw.println("Active jobs:");
1845            for (int i=0; i<mActiveServices.size(); i++) {
1846                JobServiceContext jsc = mActiveServices.get(i);
1847                pw.print("  Slot #"); pw.print(i); pw.print(": ");
1848                if (jsc.getRunningJob() == null) {
1849                    pw.println("inactive");
1850                    continue;
1851                } else {
1852                    pw.println(jsc.getRunningJob().toShortString());
1853                    pw.print("    Running for: ");
1854                    TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
1855                    pw.print(", timeout at: ");
1856                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
1857                    pw.println();
1858                    jsc.getRunningJob().dump(pw, "    ", false);
1859                    int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
1860                    if (priority != JobInfo.PRIORITY_DEFAULT) {
1861                        pw.print("    Evaluated priority: "); pw.println(priority);
1862                    }
1863                }
1864            }
1865            if (filterUid == -1) {
1866                pw.println();
1867                pw.print("mReadyToRock="); pw.println(mReadyToRock);
1868                pw.print("mReportedActive="); pw.println(mReportedActive);
1869                pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
1870            }
1871        }
1872        pw.println();
1873    }
1874}
1875