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