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 android.annotation.UserIdInt;
23import android.app.Activity;
24import android.app.ActivityManager;
25import android.app.ActivityManagerInternal;
26import android.app.AlarmManager;
27import android.app.AppGlobals;
28import android.app.IUidObserver;
29import android.app.job.IJobScheduler;
30import android.app.job.JobInfo;
31import android.app.job.JobParameters;
32import android.app.job.JobProtoEnums;
33import android.app.job.JobScheduler;
34import android.app.job.JobService;
35import android.app.job.JobWorkItem;
36import android.app.usage.UsageStatsManager;
37import android.app.usage.UsageStatsManagerInternal;
38import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
39import android.content.BroadcastReceiver;
40import android.content.ComponentName;
41import android.content.ContentResolver;
42import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
45import android.content.pm.IPackageManager;
46import android.content.pm.PackageManager;
47import android.content.pm.PackageManager.NameNotFoundException;
48import android.content.pm.PackageManagerInternal;
49import android.content.pm.ServiceInfo;
50import android.database.ContentObserver;
51import android.net.Uri;
52import android.os.BatteryStats;
53import android.os.BatteryStatsInternal;
54import android.os.Binder;
55import android.os.Handler;
56import android.os.Looper;
57import android.os.Message;
58import android.os.Process;
59import android.os.RemoteException;
60import android.os.ResultReceiver;
61import android.os.ServiceManager;
62import android.os.ShellCallback;
63import android.os.SystemClock;
64import android.os.UserHandle;
65import android.os.UserManagerInternal;
66import android.provider.Settings;
67import android.text.format.DateUtils;
68import android.util.KeyValueListParser;
69import android.util.Log;
70import android.util.Slog;
71import android.util.SparseArray;
72import android.util.SparseIntArray;
73import android.util.StatsLog;
74import android.util.TimeUtils;
75import android.util.proto.ProtoOutputStream;
76
77import com.android.internal.annotations.VisibleForTesting;
78import com.android.internal.app.IBatteryStats;
79import com.android.internal.app.procstats.ProcessStats;
80import com.android.internal.os.BackgroundThread;
81import com.android.internal.util.ArrayUtils;
82import com.android.internal.util.DumpUtils;
83import com.android.internal.util.IndentingPrintWriter;
84import com.android.internal.util.Preconditions;
85import com.android.server.AppStateTracker;
86import com.android.server.DeviceIdleController;
87import com.android.server.FgThread;
88import com.android.server.LocalServices;
89import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
90import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
91import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
92import com.android.server.job.controllers.BackgroundJobsController;
93import com.android.server.job.controllers.BatteryController;
94import com.android.server.job.controllers.ConnectivityController;
95import com.android.server.job.controllers.ContentObserverController;
96import com.android.server.job.controllers.DeviceIdleJobsController;
97import com.android.server.job.controllers.IdleController;
98import com.android.server.job.controllers.JobStatus;
99import com.android.server.job.controllers.StateController;
100import com.android.server.job.controllers.StorageController;
101import com.android.server.job.controllers.TimeController;
102
103import libcore.util.EmptyArray;
104
105import java.io.FileDescriptor;
106import java.io.PrintWriter;
107import java.time.Clock;
108import java.util.ArrayList;
109import java.util.Arrays;
110import java.util.Collections;
111import java.util.Comparator;
112import java.util.HashMap;
113import java.util.Iterator;
114import java.util.List;
115import java.util.function.Consumer;
116import java.util.function.Predicate;
117
118/**
119 * Responsible for taking jobs representing work to be performed by a client app, and determining
120 * based on the criteria specified when that job should be run against the client application's
121 * endpoint.
122 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
123 * about constraints, or the state of active jobs. It receives callbacks from the various
124 * controllers and completed jobs and operates accordingly.
125 *
126 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
127 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
128 * @hide
129 */
130public class JobSchedulerService extends com.android.server.SystemService
131        implements StateChangedListener, JobCompletedListener {
132    public static final String TAG = "JobScheduler";
133    public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
134    public static final boolean DEBUG_STANDBY = DEBUG || false;
135
136    /** The maximum number of concurrent jobs we run at one time. */
137    private static final int MAX_JOB_CONTEXTS_COUNT = 16;
138    /** Enforce a per-app limit on scheduled jobs? */
139    private static final boolean ENFORCE_MAX_JOBS = true;
140    /** The maximum number of jobs that we allow an unprivileged app to schedule */
141    private static final int MAX_JOBS_PER_APP = 100;
142
143    @VisibleForTesting
144    public static Clock sSystemClock = Clock.systemUTC();
145    @VisibleForTesting
146    public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
147    @VisibleForTesting
148    public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
149
150    /** Global local for all job scheduler state. */
151    final Object mLock = new Object();
152    /** Master list of jobs. */
153    final JobStore mJobs;
154    /** Tracking the standby bucket state of each app */
155    final StandbyTracker mStandbyTracker;
156    /** Tracking amount of time each package runs for. */
157    final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
158
159    static final int MSG_JOB_EXPIRED = 0;
160    static final int MSG_CHECK_JOB = 1;
161    static final int MSG_STOP_JOB = 2;
162    static final int MSG_CHECK_JOB_GREEDY = 3;
163    static final int MSG_UID_STATE_CHANGED = 4;
164    static final int MSG_UID_GONE = 5;
165    static final int MSG_UID_ACTIVE = 6;
166    static final int MSG_UID_IDLE = 7;
167
168    /**
169     * Track Services that have currently active or pending jobs. The index is provided by
170     * {@link JobStatus#getServiceToken()}
171     */
172    final List<JobServiceContext> mActiveServices = new ArrayList<>();
173
174    /** List of controllers that will notify this service of updates to jobs. */
175    private final List<StateController> mControllers;
176    /** Need direct access to this for testing. */
177    private final BatteryController mBatteryController;
178    /** Need direct access to this for testing. */
179    private final StorageController mStorageController;
180    /** Need directly for sending uid state changes */
181    private final DeviceIdleJobsController mDeviceIdleJobsController;
182
183    /**
184     * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
185     * when ready to execute them.
186     */
187    final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
188
189    int[] mStartedUsers = EmptyArray.INT;
190
191    final JobHandler mHandler;
192    final JobSchedulerStub mJobSchedulerStub;
193
194    PackageManagerInternal mLocalPM;
195    ActivityManagerInternal mActivityManagerInternal;
196    IBatteryStats mBatteryStats;
197    DeviceIdleController.LocalService mLocalDeviceIdleController;
198    AppStateTracker mAppStateTracker;
199    final UsageStatsManagerInternal mUsageStats;
200
201    /**
202     * Set to true once we are allowed to run third party apps.
203     */
204    boolean mReadyToRock;
205
206    /**
207     * What we last reported to DeviceIdleController about whether we are active.
208     */
209    boolean mReportedActive;
210
211    /**
212     * Are we currently in device-wide standby parole?
213     */
214    volatile boolean mInParole;
215
216    /**
217     * Current limit on the number of concurrent JobServiceContext entries we want to
218     * keep actively running a job.
219     */
220    int mMaxActiveJobs = 1;
221
222    /**
223     * A mapping of which uids are currently in the foreground to their effective priority.
224     */
225    final SparseIntArray mUidPriorityOverride = new SparseIntArray();
226
227    /**
228     * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
229     */
230    final SparseIntArray mBackingUpUids = new SparseIntArray();
231
232    /**
233     * Count standby heartbeats, and keep track of which beat each bucket's jobs will
234     * next become runnable.  Index into this array is by normalized bucket:
235     * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }.  The ACTIVE and NEVER bucket
236     * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
237     * and NEVER apps don't get them at all.
238     */
239    final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
240    long mHeartbeat = 0;
241    long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
242
243    /**
244     * Named indices into the STANDBY_BEATS array, for clarity in referring to
245     * specific buckets' bookkeeping.
246     */
247    static final int ACTIVE_INDEX = 0;
248    static final int WORKING_INDEX = 1;
249    static final int FREQUENT_INDEX = 2;
250    static final int RARE_INDEX = 3;
251    static final int NEVER_INDEX = 4;
252
253    /**
254     * Bookkeeping about when jobs last run.  We keep our own record in heartbeat time,
255     * rather than rely on Usage Stats' timestamps, because heartbeat time can be
256     * manipulated for testing purposes and we need job runnability to track that rather
257     * than real time.
258     *
259     * Outer SparseArray slices by user handle; inner map of package name to heartbeat
260     * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
261     * and it will be accessed in a known-hot code path.
262     */
263    final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
264
265    static final String HEARTBEAT_TAG = "*job.heartbeat*";
266    final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
267
268    // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
269
270    /**
271     * This array essentially stores the state of mActiveServices array.
272     * The ith index stores the job present on the ith JobServiceContext.
273     * We manipulate this array until we arrive at what jobs should be running on
274     * what JobServiceContext.
275     */
276    JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
277    /**
278     * Indicates whether we need to act on this jobContext id
279     */
280    boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
281    /**
282     * The uid whose jobs we would like to assign to a context.
283     */
284    int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
285
286    private class ConstantsObserver extends ContentObserver {
287        private ContentResolver mResolver;
288
289        public ConstantsObserver(Handler handler) {
290            super(handler);
291        }
292
293        public void start(ContentResolver resolver) {
294            mResolver = resolver;
295            mResolver.registerContentObserver(Settings.Global.getUriFor(
296                    Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
297            updateConstants();
298        }
299
300        @Override
301        public void onChange(boolean selfChange, Uri uri) {
302            updateConstants();
303        }
304
305        private void updateConstants() {
306            synchronized (mLock) {
307                try {
308                    mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
309                            Settings.Global.JOB_SCHEDULER_CONSTANTS));
310                } catch (IllegalArgumentException e) {
311                    // Failed to parse the settings string, log this and move on
312                    // with defaults.
313                    Slog.e(TAG, "Bad jobscheduler settings", e);
314                }
315            }
316
317            // Reset the heartbeat alarm based on the new heartbeat duration
318            setNextHeartbeatAlarm();
319        }
320    }
321
322    /**
323     * All times are in milliseconds. These constants are kept synchronized with the system
324     * global Settings. Any access to this class or its fields should be done while
325     * holding the JobSchedulerService.mLock lock.
326     */
327    public static class Constants {
328        // Key names stored in the settings value.
329        private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
330        private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
331        private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
332        private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
333        private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
334        private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
335        private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
336        private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
337        private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
338        private static final String KEY_FG_JOB_COUNT = "fg_job_count";
339        private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
340        private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
341        private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
342        private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
343        private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
344                = "max_standard_reschedule_count";
345        private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
346        private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
347        private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
348        private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
349        private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
350        private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
351        private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
352        private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
353        private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
354
355        private static final int DEFAULT_MIN_IDLE_COUNT = 1;
356        private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
357        private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
358        private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
359        private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
360        private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
361        private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
362        private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
363        private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
364        private static final int DEFAULT_FG_JOB_COUNT = 4;
365        private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
366        private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
367        private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
368        private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
369        private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
370        private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
371        private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
372        private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
373        private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
374        private static final int DEFAULT_STANDBY_WORKING_BEATS = 11;  // ~ 2 hours, with 11min beats
375        private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
376        private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
377        private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
378        private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
379
380        /**
381         * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
382         * early.
383         */
384        int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
385        /**
386         * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
387         * things early.
388         */
389        int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
390        /**
391         * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
392         * schedule things early.
393         */
394        int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
395        /**
396         * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
397         * schedule things early.
398         */
399        int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
400        /**
401         * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
402         * things early.  1 == Run connectivity jobs as soon as ready.
403         */
404        int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
405        /**
406         * Minimum # of content trigger jobs that must be ready in order to force the JMS to
407         * schedule things early.
408         */
409        int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
410        /**
411         * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
412         * running some work early.  This (and thus the other min counts) is now set to 1, to
413         * prevent any batching at this level.  Since we now do batching through doze, that is
414         * a much better mechanism.
415         */
416        int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
417        /**
418         * This is the job execution factor that is considered to be heavy use of the system.
419         */
420        float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
421        /**
422         * This is the job execution factor that is considered to be moderate use of the system.
423         */
424        float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
425        /**
426         * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
427         */
428        int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
429        /**
430         * The maximum number of background jobs we allow when the system is in a normal
431         * memory state.
432         */
433        int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
434        /**
435         * The maximum number of background jobs we allow when the system is in a moderate
436         * memory state.
437         */
438        int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
439        /**
440         * The maximum number of background jobs we allow when the system is in a low
441         * memory state.
442         */
443        int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
444        /**
445         * The maximum number of background jobs we allow when the system is in a critical
446         * memory state.
447         */
448        int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
449        /**
450         * The maximum number of times we allow a job to have itself rescheduled before
451         * giving up on it, for standard jobs.
452         */
453        int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
454        /**
455         * The maximum number of times we allow a job to have itself rescheduled before
456         * giving up on it, for jobs that are executing work.
457         */
458        int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
459        /**
460         * The minimum backoff time to allow for linear backoff.
461         */
462        long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
463        /**
464         * The minimum backoff time to allow for exponential backoff.
465         */
466        long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
467        /**
468         * How often we recalculate runnability based on apps' standby bucket assignment.
469         * This should be prime relative to common time interval lengths such as a quarter-
470         * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
471         */
472        long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
473        /**
474         * Mapping: standby bucket -> number of heartbeats between each sweep of that
475         * bucket's jobs.
476         *
477         * Bucket assignments as recorded in the JobStatus objects are normalized to be
478         * indices into this array, rather than the raw constants used
479         * by AppIdleHistory.
480         */
481        final int[] STANDBY_BEATS = {
482                0,
483                DEFAULT_STANDBY_WORKING_BEATS,
484                DEFAULT_STANDBY_FREQUENT_BEATS,
485                DEFAULT_STANDBY_RARE_BEATS
486        };
487        /**
488         * The fraction of a job's running window that must pass before we
489         * consider running it when the network is congested.
490         */
491        public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
492        /**
493         * The fraction of a prefetch job's running window that must pass before
494         * we consider matching it against a metered network.
495         */
496        public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
497
498        private final KeyValueListParser mParser = new KeyValueListParser(',');
499
500        void updateConstantsLocked(String value) {
501            try {
502                mParser.setString(value);
503            } catch (Exception e) {
504                // Failed to parse the settings string, log this and move on
505                // with defaults.
506                Slog.e(TAG, "Bad jobscheduler settings", e);
507            }
508
509            MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
510                    DEFAULT_MIN_IDLE_COUNT);
511            MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
512                    DEFAULT_MIN_CHARGING_COUNT);
513            MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
514                    DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
515            MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
516                    DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
517            MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
518                    DEFAULT_MIN_CONNECTIVITY_COUNT);
519            MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
520                    DEFAULT_MIN_CONTENT_COUNT);
521            MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
522                    DEFAULT_MIN_READY_JOBS_COUNT);
523            HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
524                    DEFAULT_HEAVY_USE_FACTOR);
525            MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
526                    DEFAULT_MODERATE_USE_FACTOR);
527            FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
528                    DEFAULT_FG_JOB_COUNT);
529            BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
530                    DEFAULT_BG_NORMAL_JOB_COUNT);
531            if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
532                BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
533            }
534            BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
535                    DEFAULT_BG_MODERATE_JOB_COUNT);
536            if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
537                BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
538            }
539            BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
540                    DEFAULT_BG_LOW_JOB_COUNT);
541            if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
542                BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
543            }
544            BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
545                    DEFAULT_BG_CRITICAL_JOB_COUNT);
546            if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
547                BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
548            }
549            MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
550                    DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
551            MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
552                    DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
553            MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
554                    DEFAULT_MIN_LINEAR_BACKOFF_TIME);
555            MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
556                    DEFAULT_MIN_EXP_BACKOFF_TIME);
557            STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
558                    DEFAULT_STANDBY_HEARTBEAT_TIME);
559            STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
560                    DEFAULT_STANDBY_WORKING_BEATS);
561            STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
562                    DEFAULT_STANDBY_FREQUENT_BEATS);
563            STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
564                    DEFAULT_STANDBY_RARE_BEATS);
565            CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
566                    DEFAULT_CONN_CONGESTION_DELAY_FRAC);
567            CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
568                    DEFAULT_CONN_PREFETCH_RELAX_FRAC);
569        }
570
571        void dump(IndentingPrintWriter pw) {
572            pw.println("Settings:");
573            pw.increaseIndent();
574            pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
575            pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
576            pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
577            pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
578            pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
579            pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
580            pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
581            pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
582            pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
583            pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
584            pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
585            pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
586            pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
587            pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
588            pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
589            pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
590            pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
591            pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
592            pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
593            pw.print("standby_beats={");
594            pw.print(STANDBY_BEATS[0]);
595            for (int i = 1; i < STANDBY_BEATS.length; i++) {
596                pw.print(", ");
597                pw.print(STANDBY_BEATS[i]);
598            }
599            pw.println('}');
600            pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
601            pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
602            pw.decreaseIndent();
603        }
604
605        void dump(ProtoOutputStream proto, long fieldId) {
606            final long token = proto.start(fieldId);
607            proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
608            proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
609            proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
610            proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
611            proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
612            proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
613            proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
614            proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
615            proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
616            proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
617            proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
618            proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
619            proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
620            proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
621            proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
622            proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
623            proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
624            proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
625            proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
626            for (int period : STANDBY_BEATS) {
627                proto.write(ConstantsProto.STANDBY_BEATS, period);
628            }
629            proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
630            proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
631            proto.end(token);
632        }
633    }
634
635    final Constants mConstants;
636    final ConstantsObserver mConstantsObserver;
637
638    static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
639        if (o1.enqueueTime < o2.enqueueTime) {
640            return -1;
641        }
642        return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
643    };
644
645    static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
646        int where = Collections.binarySearch(array, newItem, comparator);
647        if (where < 0) {
648            where = ~where;
649        }
650        array.add(where, newItem);
651    }
652
653    /**
654     * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
655     * still clean up. On reinstall the package will have a new uid.
656     */
657    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
658        @Override
659        public void onReceive(Context context, Intent intent) {
660            final String action = intent.getAction();
661            if (DEBUG) {
662                Slog.d(TAG, "Receieved: " + action);
663            }
664            final String pkgName = getPackageName(intent);
665            final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
666
667            if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
668                // Purge the app's jobs if the whole package was just disabled.  When this is
669                // the case the component name will be a bare package name.
670                if (pkgName != null && pkgUid != -1) {
671                    final String[] changedComponents = intent.getStringArrayExtra(
672                            Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
673                    if (changedComponents != null) {
674                        for (String component : changedComponents) {
675                            if (component.equals(pkgName)) {
676                                if (DEBUG) {
677                                    Slog.d(TAG, "Package state change: " + pkgName);
678                                }
679                                try {
680                                    final int userId = UserHandle.getUserId(pkgUid);
681                                    IPackageManager pm = AppGlobals.getPackageManager();
682                                    final int state = pm.getApplicationEnabledSetting(pkgName, userId);
683                                    if (state == COMPONENT_ENABLED_STATE_DISABLED
684                                            || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
685                                        if (DEBUG) {
686                                            Slog.d(TAG, "Removing jobs for package " + pkgName
687                                                    + " in user " + userId);
688                                        }
689                                        cancelJobsForPackageAndUid(pkgName, pkgUid,
690                                                "app disabled");
691                                    }
692                                } catch (RemoteException|IllegalArgumentException e) {
693                                    /*
694                                     * IllegalArgumentException means that the package doesn't exist.
695                                     * This arises when PACKAGE_CHANGED broadcast delivery has lagged
696                                     * behind outright uninstall, so by the time we try to act it's gone.
697                                     * We don't need to act on this PACKAGE_CHANGED when this happens;
698                                     * we'll get a PACKAGE_REMOVED later and clean up then.
699                                     *
700                                     * RemoteException can't actually happen; the package manager is
701                                     * running in this same process.
702                                     */
703                                }
704                                break;
705                            }
706                        }
707                    }
708                } else {
709                    Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
710                }
711            } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
712                // If this is an outright uninstall rather than the first half of an
713                // app update sequence, cancel the jobs associated with the app.
714                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
715                    int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
716                    if (DEBUG) {
717                        Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
718                    }
719                    cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
720                }
721            } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
722                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
723                if (DEBUG) {
724                    Slog.d(TAG, "Removing jobs for user: " + userId);
725                }
726                cancelJobsForUser(userId);
727            } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
728                // Has this package scheduled any jobs, such that we will take action
729                // if it were to be force-stopped?
730                if (pkgUid != -1) {
731                    List<JobStatus> jobsForUid;
732                    synchronized (mLock) {
733                        jobsForUid = mJobs.getJobsByUid(pkgUid);
734                    }
735                    for (int i = jobsForUid.size() - 1; i >= 0; i--) {
736                        if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
737                            if (DEBUG) {
738                                Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
739                                        + pkgUid + " has jobs");
740                            }
741                            setResultCode(Activity.RESULT_OK);
742                            break;
743                        }
744                    }
745                }
746            } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
747                // possible force-stop
748                if (pkgUid != -1) {
749                    if (DEBUG) {
750                        Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
751                    }
752                    cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
753                }
754            }
755        }
756    };
757
758    private String getPackageName(Intent intent) {
759        Uri uri = intent.getData();
760        String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
761        return pkg;
762    }
763
764    final private IUidObserver mUidObserver = new IUidObserver.Stub() {
765        @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
766            mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
767        }
768
769        @Override public void onUidGone(int uid, boolean disabled) {
770            mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
771        }
772
773        @Override public void onUidActive(int uid) throws RemoteException {
774            mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
775        }
776
777        @Override public void onUidIdle(int uid, boolean disabled) {
778            mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
779        }
780
781        @Override public void onUidCachedChanged(int uid, boolean cached) {
782        }
783    };
784
785    public Context getTestableContext() {
786        return getContext();
787    }
788
789    public Object getLock() {
790        return mLock;
791    }
792
793    public JobStore getJobStore() {
794        return mJobs;
795    }
796
797    public Constants getConstants() {
798        return mConstants;
799    }
800
801    @Override
802    public void onStartUser(int userHandle) {
803        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
804        // Let's kick any outstanding jobs for this user.
805        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
806    }
807
808    @Override
809    public void onUnlockUser(int userHandle) {
810        // Let's kick any outstanding jobs for this user.
811        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
812    }
813
814    @Override
815    public void onStopUser(int userHandle) {
816        mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
817    }
818
819    /**
820     * Return whether an UID is active or idle.
821     */
822    private boolean isUidActive(int uid) {
823        return mAppStateTracker.isUidActiveSynced(uid);
824    }
825
826    private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
827
828    public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
829            int userId, String tag) {
830        try {
831            if (ActivityManager.getService().isAppStartModeDisabled(uId,
832                    job.getService().getPackageName())) {
833                Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
834                        + " -- package not allowed to start");
835                return JobScheduler.RESULT_FAILURE;
836            }
837        } catch (RemoteException e) {
838        }
839
840        synchronized (mLock) {
841            final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
842
843            if (work != null && toCancel != null) {
844                // Fast path: we are adding work to an existing job, and the JobInfo is not
845                // changing.  We can just directly enqueue this work in to the job.
846                if (toCancel.getJob().equals(job)) {
847
848                    toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
849
850                    // If any of work item is enqueued when the source is in the foreground,
851                    // exempt the entire job.
852                    toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
853
854                    return JobScheduler.RESULT_SUCCESS;
855                }
856            }
857
858            JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
859
860            // Give exemption if the source is in the foreground just now.
861            // Note if it's a sync job, this method is called on the handler so it's not exactly
862            // the state when requestSync() was called, but that should be fine because of the
863            // 1 minute foreground grace period.
864            jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
865
866            if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
867            // Jobs on behalf of others don't apply to the per-app job cap
868            if (ENFORCE_MAX_JOBS && packageName == null) {
869                if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
870                    Slog.w(TAG, "Too many jobs for uid " + uId);
871                    throw new IllegalStateException("Apps may not schedule more than "
872                                + MAX_JOBS_PER_APP + " distinct jobs");
873                }
874            }
875
876            // This may throw a SecurityException.
877            jobStatus.prepareLocked(ActivityManager.getService());
878
879            if (toCancel != null) {
880                cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
881            }
882            if (work != null) {
883                // If work has been supplied, enqueue it into the new job.
884                jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
885            }
886            startTrackingJobLocked(jobStatus, toCancel);
887            StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
888                    uId, null, jobStatus.getBatteryName(),
889                    StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
890                    JobProtoEnums.STOP_REASON_CANCELLED);
891
892            // If the job is immediately ready to run, then we can just immediately
893            // put it in the pending list and try to schedule it.  This is especially
894            // important for jobs with a 0 deadline constraint, since they will happen a fair
895            // amount, we want to handle them as quickly as possible, and semantically we want to
896            // make sure we have started holding the wake lock for the job before returning to
897            // the caller.
898            // If the job is not yet ready to run, there is nothing more to do -- we are
899            // now just waiting for one of its controllers to change state and schedule
900            // the job appropriately.
901            if (isReadyToBeExecutedLocked(jobStatus)) {
902                // This is a new job, we can just immediately put it on the pending
903                // list and try to run it.
904                mJobPackageTracker.notePending(jobStatus);
905                addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
906                maybeRunPendingJobsLocked();
907            }
908        }
909        return JobScheduler.RESULT_SUCCESS;
910    }
911
912    public List<JobInfo> getPendingJobs(int uid) {
913        synchronized (mLock) {
914            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
915            ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
916            for (int i = jobs.size() - 1; i >= 0; i--) {
917                JobStatus job = jobs.get(i);
918                outList.add(job.getJob());
919            }
920            return outList;
921        }
922    }
923
924    public JobInfo getPendingJob(int uid, int jobId) {
925        synchronized (mLock) {
926            List<JobStatus> jobs = mJobs.getJobsByUid(uid);
927            for (int i = jobs.size() - 1; i >= 0; i--) {
928                JobStatus job = jobs.get(i);
929                if (job.getJobId() == jobId) {
930                    return job.getJob();
931                }
932            }
933            return null;
934        }
935    }
936
937    void cancelJobsForUser(int userHandle) {
938        synchronized (mLock) {
939            final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
940            for (int i=0; i<jobsForUser.size(); i++) {
941                JobStatus toRemove = jobsForUser.get(i);
942                cancelJobImplLocked(toRemove, null, "user removed");
943            }
944        }
945    }
946
947    private void cancelJobsForNonExistentUsers() {
948        UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
949        synchronized (mLock) {
950            mJobs.removeJobsOfNonUsers(umi.getUserIds());
951        }
952    }
953
954    void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
955        if ("android".equals(pkgName)) {
956            Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
957            return;
958        }
959        synchronized (mLock) {
960            final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
961            for (int i = jobsForUid.size() - 1; i >= 0; i--) {
962                final JobStatus job = jobsForUid.get(i);
963                if (job.getSourcePackageName().equals(pkgName)) {
964                    cancelJobImplLocked(job, null, reason);
965                }
966            }
967        }
968    }
969
970    /**
971     * Entry point from client to cancel all jobs originating from their uid.
972     * This will remove the job from the master list, and cancel the job if it was staged for
973     * execution or being executed.
974     * @param uid Uid to check against for removal of a job.
975     *
976     */
977    public boolean cancelJobsForUid(int uid, String reason) {
978        if (uid == Process.SYSTEM_UID) {
979            Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
980            return false;
981        }
982
983        boolean jobsCanceled = false;
984        synchronized (mLock) {
985            final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
986            for (int i=0; i<jobsForUid.size(); i++) {
987                JobStatus toRemove = jobsForUid.get(i);
988                cancelJobImplLocked(toRemove, null, reason);
989                jobsCanceled = true;
990            }
991        }
992        return jobsCanceled;
993    }
994
995    /**
996     * Entry point from client to cancel the job corresponding to the jobId provided.
997     * This will remove the job from the master list, and cancel the job if it was staged for
998     * execution or being executed.
999     * @param uid Uid of the calling client.
1000     * @param jobId Id of the job, provided at schedule-time.
1001     */
1002    public boolean cancelJob(int uid, int jobId, int callingUid) {
1003        JobStatus toCancel;
1004        synchronized (mLock) {
1005            toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
1006            if (toCancel != null) {
1007                cancelJobImplLocked(toCancel, null,
1008                        "cancel() called by app, callingUid=" + callingUid
1009                        + " uid=" + uid + " jobId=" + jobId);
1010            }
1011            return (toCancel != null);
1012        }
1013    }
1014
1015    private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
1016        if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1017        cancelled.unprepareLocked(ActivityManager.getService());
1018        stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1019        // Remove from pending queue.
1020        if (mPendingJobs.remove(cancelled)) {
1021            mJobPackageTracker.noteNonpending(cancelled);
1022        }
1023        // Cancel if running.
1024        stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
1025        reportActiveLocked();
1026    }
1027
1028    void updateUidState(int uid, int procState) {
1029        synchronized (mLock) {
1030            if (procState == ActivityManager.PROCESS_STATE_TOP) {
1031                // Only use this if we are exactly the top app.  All others can live
1032                // with just the foreground priority.  This means that persistent processes
1033                // can never be the top app priority...  that is fine.
1034                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
1035            } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
1036                mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
1037            } else {
1038                mUidPriorityOverride.delete(uid);
1039            }
1040        }
1041    }
1042
1043    @Override
1044    public void onDeviceIdleStateChanged(boolean deviceIdle) {
1045        synchronized (mLock) {
1046            if (deviceIdle) {
1047                // When becoming idle, make sure no jobs are actively running,
1048                // except those using the idle exemption flag.
1049                for (int i=0; i<mActiveServices.size(); i++) {
1050                    JobServiceContext jsc = mActiveServices.get(i);
1051                    final JobStatus executing = jsc.getRunningJobLocked();
1052                    if (executing != null
1053                            && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
1054                        jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1055                                "cancelled due to doze");
1056                    }
1057                }
1058            } else {
1059                // When coming out of idle, allow thing to start back up.
1060                if (mReadyToRock) {
1061                    if (mLocalDeviceIdleController != null) {
1062                        if (!mReportedActive) {
1063                            mReportedActive = true;
1064                            mLocalDeviceIdleController.setJobsActive(true);
1065                        }
1066                    }
1067                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1068                }
1069            }
1070        }
1071    }
1072
1073    void reportActiveLocked() {
1074        // active is true if pending queue contains jobs OR some job is running.
1075        boolean active = mPendingJobs.size() > 0;
1076        if (mPendingJobs.size() <= 0) {
1077            for (int i=0; i<mActiveServices.size(); i++) {
1078                final JobServiceContext jsc = mActiveServices.get(i);
1079                final JobStatus job = jsc.getRunningJobLocked();
1080                if (job != null
1081                        && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
1082                        && !job.dozeWhitelisted
1083                        && !job.uidActive) {
1084                    // We will report active if we have a job running and it is not an exception
1085                    // due to being in the foreground or whitelisted.
1086                    active = true;
1087                    break;
1088                }
1089            }
1090        }
1091
1092        if (mReportedActive != active) {
1093            mReportedActive = active;
1094            if (mLocalDeviceIdleController != null) {
1095                mLocalDeviceIdleController.setJobsActive(active);
1096            }
1097        }
1098    }
1099
1100    void reportAppUsage(String packageName, int userId) {
1101        // This app just transitioned into interactive use or near equivalent, so we should
1102        // take a look at its job state for feedback purposes.
1103    }
1104
1105    /**
1106     * Initializes the system service.
1107     * <p>
1108     * Subclasses must define a single argument constructor that accepts the context
1109     * and passes it to super.
1110     * </p>
1111     *
1112     * @param context The system server context.
1113     */
1114    public JobSchedulerService(Context context) {
1115        super(context);
1116
1117        mLocalPM = LocalServices.getService(PackageManagerInternal.class);
1118        mActivityManagerInternal = Preconditions.checkNotNull(
1119                LocalServices.getService(ActivityManagerInternal.class));
1120
1121        mHandler = new JobHandler(context.getMainLooper());
1122        mConstants = new Constants();
1123        mConstantsObserver = new ConstantsObserver(mHandler);
1124        mJobSchedulerStub = new JobSchedulerStub();
1125
1126        // Set up the app standby bucketing tracker
1127        mStandbyTracker = new StandbyTracker();
1128        mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1129        mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
1130
1131        // The job store needs to call back
1132        publishLocalService(JobSchedulerInternal.class, new LocalService());
1133
1134        // Initialize the job store and set up any persisted jobs
1135        mJobs = JobStore.initAndGet(this);
1136
1137        // Create the controllers.
1138        mControllers = new ArrayList<StateController>();
1139        mControllers.add(new ConnectivityController(this));
1140        mControllers.add(new TimeController(this));
1141        mControllers.add(new IdleController(this));
1142        mBatteryController = new BatteryController(this);
1143        mControllers.add(mBatteryController);
1144        mStorageController = new StorageController(this);
1145        mControllers.add(mStorageController);
1146        mControllers.add(new BackgroundJobsController(this));
1147        mControllers.add(new ContentObserverController(this));
1148        mDeviceIdleJobsController = new DeviceIdleJobsController(this);
1149        mControllers.add(mDeviceIdleJobsController);
1150
1151        // If the job store determined that it can't yet reschedule persisted jobs,
1152        // we need to start watching the clock.
1153        if (!mJobs.jobTimesInflatedValid()) {
1154            Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1155            context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1156        }
1157    }
1158
1159    private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1160        @Override
1161        public void onReceive(Context context, Intent intent) {
1162            if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1163                // When we reach clock sanity, recalculate the temporal windows
1164                // of all affected jobs.
1165                if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
1166                    Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1167
1168                    // We've done our job now, so stop watching the time.
1169                    context.unregisterReceiver(this);
1170
1171                    // And kick off the work to update the affected jobs, using a secondary
1172                    // thread instead of chugging away here on the main looper thread.
1173                    FgThread.getHandler().post(mJobTimeUpdater);
1174                }
1175            }
1176        }
1177    };
1178
1179    private final Runnable mJobTimeUpdater = () -> {
1180        final ArrayList<JobStatus> toRemove = new ArrayList<>();
1181        final ArrayList<JobStatus> toAdd = new ArrayList<>();
1182        synchronized (mLock) {
1183            // Note: we intentionally both look up the existing affected jobs and replace them
1184            // with recalculated ones inside the same lock lifetime.
1185            getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1186
1187            // Now, at each position [i], we have both the existing JobStatus
1188            // and the one that replaces it.
1189            final int N = toAdd.size();
1190            for (int i = 0; i < N; i++) {
1191                final JobStatus oldJob = toRemove.get(i);
1192                final JobStatus newJob = toAdd.get(i);
1193                if (DEBUG) {
1194                    Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
1195                }
1196                cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1197            }
1198        }
1199    };
1200
1201    @Override
1202    public void onStart() {
1203        publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1204    }
1205
1206    @Override
1207    public void onBootPhase(int phase) {
1208        if (PHASE_SYSTEM_SERVICES_READY == phase) {
1209            mConstantsObserver.start(getContext().getContentResolver());
1210
1211            mAppStateTracker = Preconditions.checkNotNull(
1212                    LocalServices.getService(AppStateTracker.class));
1213            setNextHeartbeatAlarm();
1214
1215            // Register br for package removals and user removals.
1216            final IntentFilter filter = new IntentFilter();
1217            filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1218            filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1219            filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1220            filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1221            filter.addDataScheme("package");
1222            getContext().registerReceiverAsUser(
1223                    mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1224            final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1225            getContext().registerReceiverAsUser(
1226                    mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
1227            try {
1228                ActivityManager.getService().registerUidObserver(mUidObserver,
1229                        ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
1230                        | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1231                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
1232            } catch (RemoteException e) {
1233                // ignored; both services live in system_server
1234            }
1235            // Remove any jobs that are not associated with any of the current users.
1236            cancelJobsForNonExistentUsers();
1237        } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
1238            synchronized (mLock) {
1239                // Let's go!
1240                mReadyToRock = true;
1241                mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1242                        BatteryStats.SERVICE_NAME));
1243                mLocalDeviceIdleController
1244                        = LocalServices.getService(DeviceIdleController.LocalService.class);
1245                // Create the "runners".
1246                for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1247                    mActiveServices.add(
1248                            new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
1249                                    getContext().getMainLooper()));
1250                }
1251                // Attach jobs to their controllers.
1252                mJobs.forEachJob((job) -> {
1253                    for (int controller = 0; controller < mControllers.size(); controller++) {
1254                        final StateController sc = mControllers.get(controller);
1255                        sc.maybeStartTrackingJobLocked(job, null);
1256                    }
1257                });
1258                // GO GO GO!
1259                mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1260            }
1261        }
1262    }
1263
1264    /**
1265     * Called when we have a job status object that we need to insert in our
1266     * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1267     * about.
1268     */
1269    private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1270        if (!jobStatus.isPreparedLocked()) {
1271            Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1272        }
1273        jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
1274        final boolean update = mJobs.add(jobStatus);
1275        if (mReadyToRock) {
1276            for (int i = 0; i < mControllers.size(); i++) {
1277                StateController controller = mControllers.get(i);
1278                if (update) {
1279                    controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1280                }
1281                controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1282            }
1283        }
1284    }
1285
1286    /**
1287     * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1288     * object removed.
1289     */
1290    private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1291            boolean writeBack) {
1292        // Deal with any remaining work items in the old job.
1293        jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
1294
1295        // Remove from store as well as controllers.
1296        final boolean removed = mJobs.remove(jobStatus, writeBack);
1297        if (removed && mReadyToRock) {
1298            for (int i=0; i<mControllers.size(); i++) {
1299                StateController controller = mControllers.get(i);
1300                controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1301            }
1302        }
1303        return removed;
1304    }
1305
1306    private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
1307        for (int i=0; i<mActiveServices.size(); i++) {
1308            JobServiceContext jsc = mActiveServices.get(i);
1309            final JobStatus executing = jsc.getRunningJobLocked();
1310            if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1311                jsc.cancelExecutingJobLocked(reason, debugReason);
1312                return true;
1313            }
1314        }
1315        return false;
1316    }
1317
1318    /**
1319     * @param job JobStatus we are querying against.
1320     * @return Whether or not the job represented by the status object is currently being run or
1321     * is pending.
1322     */
1323    private boolean isCurrentlyActiveLocked(JobStatus job) {
1324        for (int i=0; i<mActiveServices.size(); i++) {
1325            JobServiceContext serviceContext = mActiveServices.get(i);
1326            final JobStatus running = serviceContext.getRunningJobLocked();
1327            if (running != null && running.matches(job.getUid(), job.getJobId())) {
1328                return true;
1329            }
1330        }
1331        return false;
1332    }
1333
1334    void noteJobsPending(List<JobStatus> jobs) {
1335        for (int i = jobs.size() - 1; i >= 0; i--) {
1336            JobStatus job = jobs.get(i);
1337            mJobPackageTracker.notePending(job);
1338        }
1339    }
1340
1341    void noteJobsNonpending(List<JobStatus> jobs) {
1342        for (int i = jobs.size() - 1; i >= 0; i--) {
1343            JobStatus job = jobs.get(i);
1344            mJobPackageTracker.noteNonpending(job);
1345        }
1346    }
1347
1348    /**
1349     * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1350     * specify an override deadline on a failed job (the failed job will run even though it's not
1351     * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1352     * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1353     *
1354     * @param failureToReschedule Provided job status that we will reschedule.
1355     * @return A newly instantiated JobStatus with the same constraints as the last job except
1356     * with adjusted timing constraints.
1357     *
1358     * @see #maybeQueueReadyJobsForExecutionLocked
1359     */
1360    private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1361        final long elapsedNowMillis = sElapsedRealtimeClock.millis();
1362        final JobInfo job = failureToReschedule.getJob();
1363
1364        final long initialBackoffMillis = job.getInitialBackoffMillis();
1365        final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1366        long delayMillis;
1367
1368        if (failureToReschedule.hasWorkLocked()) {
1369            if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1370                Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1371                        + backoffAttempts + " > work limit "
1372                        + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1373                return null;
1374            }
1375        } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1376            Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1377                    + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1378            return null;
1379        }
1380
1381        switch (job.getBackoffPolicy()) {
1382            case JobInfo.BACKOFF_POLICY_LINEAR: {
1383                long backoff = initialBackoffMillis;
1384                if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1385                    backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1386                }
1387                delayMillis = backoff * backoffAttempts;
1388            } break;
1389            default:
1390                if (DEBUG) {
1391                    Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1392                }
1393            case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1394                long backoff = initialBackoffMillis;
1395                if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1396                    backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1397                }
1398                delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1399            } break;
1400        }
1401        delayMillis =
1402                Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1403        JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1404                elapsedNowMillis + delayMillis,
1405                JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
1406                failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
1407        for (int ic=0; ic<mControllers.size(); ic++) {
1408            StateController controller = mControllers.get(ic);
1409            controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1410        }
1411        return newJob;
1412    }
1413
1414    /**
1415     * Called after a periodic has executed so we can reschedule it. We take the last execution
1416     * time of the job to be the time of completion (i.e. the time at which this function is
1417     * called).
1418     * <p>This could be inaccurate b/c the job can run for as long as
1419     * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1420     * to underscheduling at least, rather than if we had taken the last execution time to be the
1421     * start of the execution.
1422     * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1423     * tracking as though the job were newly-scheduled.
1424     * @return A new job representing the execution criteria for this instantiation of the
1425     * recurring job.
1426     */
1427    private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1428        final long elapsedNow = sElapsedRealtimeClock.millis();
1429        // Compute how much of the period is remaining.
1430        long runEarly = 0L;
1431
1432        // If this periodic was rescheduled it won't have a deadline.
1433        if (periodicToReschedule.hasDeadlineConstraint()) {
1434            runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1435        }
1436        long flex = periodicToReschedule.getJob().getFlexMillis();
1437        long period = periodicToReschedule.getJob().getIntervalMillis();
1438        long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1439        long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1440
1441        if (DEBUG) {
1442            Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1443                    newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1444        }
1445        return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1446                newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1447                0 /* backoffAttempt */,
1448                sSystemClock.millis() /* lastSuccessfulRunTime */,
1449                periodicToReschedule.getLastFailedRunTime());
1450    }
1451
1452    /*
1453     * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1454     * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1455     * reboot behavior.
1456     */
1457    long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
1458        // The furthest back in pre-boot time that we need to bother with
1459        long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1460        boolean cacheHit = false;
1461        synchronized (mLock) {
1462            HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1463            if (jobPackages != null) {
1464                long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1465                if (cachedValue < Long.MAX_VALUE) {
1466                    cacheHit = true;
1467                    heartbeat = cachedValue;
1468                }
1469            }
1470            if (!cacheHit) {
1471                // We haven't seen it yet; ask usage stats about it
1472                final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1473                if (timeSinceJob < Long.MAX_VALUE) {
1474                    // Usage stats knows about it from before, so calculate back from that
1475                    // and go from there.
1476                    heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1477                }
1478                // If usage stats returned its "not found" MAX_VALUE, we still have the
1479                // negative default 'heartbeat' value we established above
1480                setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1481            }
1482        }
1483        if (DEBUG_STANDBY) {
1484            Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1485                    + packageName + "/" + userId);
1486        }
1487        return heartbeat;
1488    }
1489
1490    long heartbeatWhenJobsLastRun(JobStatus job) {
1491        return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1492    }
1493
1494    void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1495        HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1496        if (jobPackages == null) {
1497            jobPackages = new HashMap<>();
1498            mLastJobHeartbeats.put(userId, jobPackages);
1499        }
1500        jobPackages.put(packageName, heartbeat);
1501    }
1502
1503    // JobCompletedListener implementations.
1504
1505    /**
1506     * A job just finished executing. We fetch the
1507     * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1508     * whether we want to reschedule we re-add it to the controllers.
1509     * @param jobStatus Completed job.
1510     * @param needsReschedule Whether the implementing class should reschedule this job.
1511     */
1512    @Override
1513    public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1514        if (DEBUG) {
1515            Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1516        }
1517
1518        // If the job wants to be rescheduled, we first need to make the next upcoming
1519        // job so we can transfer any appropriate state over from the previous job when
1520        // we stop it.
1521        final JobStatus rescheduledJob = needsReschedule
1522                ? getRescheduleJobForFailureLocked(jobStatus) : null;
1523
1524        // Do not write back immediately if this is a periodic job. The job may get lost if system
1525        // shuts down before it is added back.
1526        if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1527            if (DEBUG) {
1528                Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1529            }
1530            // We still want to check for jobs to execute, because this job may have
1531            // scheduled a new job under the same job id, and now we can run it.
1532            mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1533            return;
1534        }
1535
1536        if (rescheduledJob != null) {
1537            try {
1538                rescheduledJob.prepareLocked(ActivityManager.getService());
1539            } catch (SecurityException e) {
1540                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1541            }
1542            startTrackingJobLocked(rescheduledJob, jobStatus);
1543        } else if (jobStatus.getJob().isPeriodic()) {
1544            JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1545            try {
1546                rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1547            } catch (SecurityException e) {
1548                Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1549            }
1550            startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1551        }
1552        jobStatus.unprepareLocked(ActivityManager.getService());
1553        reportActiveLocked();
1554        mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1555    }
1556
1557    // StateChangedListener implementations.
1558
1559    /**
1560     * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1561     * some controller's state has changed, so as to run through the list of jobs and start/stop
1562     * any that are eligible.
1563     */
1564    @Override
1565    public void onControllerStateChanged() {
1566        mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1567    }
1568
1569    @Override
1570    public void onRunJobNow(JobStatus jobStatus) {
1571        mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1572    }
1573
1574    final private class JobHandler extends Handler {
1575
1576        public JobHandler(Looper looper) {
1577            super(looper);
1578        }
1579
1580        @Override
1581        public void handleMessage(Message message) {
1582            synchronized (mLock) {
1583                if (!mReadyToRock) {
1584                    return;
1585                }
1586                switch (message.what) {
1587                    case MSG_JOB_EXPIRED: {
1588                        JobStatus runNow = (JobStatus) message.obj;
1589                        // runNow can be null, which is a controller's way of indicating that its
1590                        // state is such that all ready jobs should be run immediately.
1591                        if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
1592                            mJobPackageTracker.notePending(runNow);
1593                            addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
1594                        } else {
1595                            queueReadyJobsForExecutionLocked();
1596                        }
1597                    } break;
1598                    case MSG_CHECK_JOB:
1599                        if (mReportedActive) {
1600                            // if jobs are currently being run, queue all ready jobs for execution.
1601                            queueReadyJobsForExecutionLocked();
1602                        } else {
1603                            // Check the list of jobs and run some of them if we feel inclined.
1604                            maybeQueueReadyJobsForExecutionLocked();
1605                        }
1606                        break;
1607                    case MSG_CHECK_JOB_GREEDY:
1608                        queueReadyJobsForExecutionLocked();
1609                        break;
1610                    case MSG_STOP_JOB:
1611                        cancelJobImplLocked((JobStatus) message.obj, null,
1612                                "app no longer allowed to run");
1613                        break;
1614
1615                    case MSG_UID_STATE_CHANGED: {
1616                        final int uid = message.arg1;
1617                        final int procState = message.arg2;
1618                        updateUidState(uid, procState);
1619                        break;
1620                    }
1621                    case MSG_UID_GONE: {
1622                        final int uid = message.arg1;
1623                        final boolean disabled = message.arg2 != 0;
1624                        updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1625                        if (disabled) {
1626                            cancelJobsForUid(uid, "uid gone");
1627                        }
1628                        synchronized (mLock) {
1629                            mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1630                        }
1631                        break;
1632                    }
1633                    case MSG_UID_ACTIVE: {
1634                        final int uid = message.arg1;
1635                        synchronized (mLock) {
1636                            mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1637                        }
1638                        break;
1639                    }
1640                    case MSG_UID_IDLE: {
1641                        final int uid = message.arg1;
1642                        final boolean disabled = message.arg2 != 0;
1643                        if (disabled) {
1644                            cancelJobsForUid(uid, "app uid idle");
1645                        }
1646                        synchronized (mLock) {
1647                            mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1648                        }
1649                        break;
1650                    }
1651
1652                }
1653                maybeRunPendingJobsLocked();
1654                // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1655                removeMessages(MSG_CHECK_JOB);
1656            }
1657        }
1658    }
1659
1660    private void stopNonReadyActiveJobsLocked() {
1661        for (int i=0; i<mActiveServices.size(); i++) {
1662            JobServiceContext serviceContext = mActiveServices.get(i);
1663            final JobStatus running = serviceContext.getRunningJobLocked();
1664            if (running != null && !running.isReady()) {
1665                serviceContext.cancelExecutingJobLocked(
1666                        JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1667                        "cancelled due to unsatisfied constraints");
1668            }
1669        }
1670    }
1671
1672    /**
1673     * Run through list of jobs and execute all possible - at least one is expired so we do
1674     * as many as we can.
1675     */
1676    private void queueReadyJobsForExecutionLocked() {
1677        if (DEBUG) {
1678            Slog.d(TAG, "queuing all ready jobs for execution:");
1679        }
1680        noteJobsNonpending(mPendingJobs);
1681        mPendingJobs.clear();
1682        stopNonReadyActiveJobsLocked();
1683        mJobs.forEachJob(mReadyQueueFunctor);
1684        mReadyQueueFunctor.postProcess();
1685
1686        if (DEBUG) {
1687            final int queuedJobs = mPendingJobs.size();
1688            if (queuedJobs == 0) {
1689                Slog.d(TAG, "No jobs pending.");
1690            } else {
1691                Slog.d(TAG, queuedJobs + " jobs queued.");
1692            }
1693        }
1694    }
1695
1696    final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
1697        ArrayList<JobStatus> newReadyJobs;
1698
1699        @Override
1700        public void accept(JobStatus job) {
1701            if (isReadyToBeExecutedLocked(job)) {
1702                if (DEBUG) {
1703                    Slog.d(TAG, "    queued " + job.toShortString());
1704                }
1705                if (newReadyJobs == null) {
1706                    newReadyJobs = new ArrayList<JobStatus>();
1707                }
1708                newReadyJobs.add(job);
1709            }
1710        }
1711
1712        public void postProcess() {
1713            if (newReadyJobs != null) {
1714                noteJobsPending(newReadyJobs);
1715                mPendingJobs.addAll(newReadyJobs);
1716                if (mPendingJobs.size() > 1) {
1717                    mPendingJobs.sort(mEnqueueTimeComparator);
1718                }
1719            }
1720            newReadyJobs = null;
1721        }
1722    }
1723    private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1724
1725    /**
1726     * The state of at least one job has changed. Here is where we could enforce various
1727     * policies on when we want to execute jobs.
1728     * Right now the policy is such:
1729     * If >1 of the ready jobs is idle mode we send all of them off
1730     * if more than 2 network connectivity jobs are ready we send them all off.
1731     * If more than 4 jobs total are ready we send them all off.
1732     * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1733     */
1734    final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
1735        int chargingCount;
1736        int batteryNotLowCount;
1737        int storageNotLowCount;
1738        int idleCount;
1739        int backoffCount;
1740        int connectivityCount;
1741        int contentCount;
1742        List<JobStatus> runnableJobs;
1743
1744        public MaybeReadyJobQueueFunctor() {
1745            reset();
1746        }
1747
1748        // Functor method invoked for each job via JobStore.forEachJob()
1749        @Override
1750        public void accept(JobStatus job) {
1751            if (isReadyToBeExecutedLocked(job)) {
1752                try {
1753                    if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1754                            job.getJob().getService().getPackageName())) {
1755                        Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1756                                + job.getJob().toString() + " -- package not allowed to start");
1757                        mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1758                        return;
1759                    }
1760                } catch (RemoteException e) {
1761                }
1762                if (job.getNumFailures() > 0) {
1763                    backoffCount++;
1764                }
1765                if (job.hasIdleConstraint()) {
1766                    idleCount++;
1767                }
1768                if (job.hasConnectivityConstraint()) {
1769                    connectivityCount++;
1770                }
1771                if (job.hasChargingConstraint()) {
1772                    chargingCount++;
1773                }
1774                if (job.hasBatteryNotLowConstraint()) {
1775                    batteryNotLowCount++;
1776                }
1777                if (job.hasStorageNotLowConstraint()) {
1778                    storageNotLowCount++;
1779                }
1780                if (job.hasContentTriggerConstraint()) {
1781                    contentCount++;
1782                }
1783                if (runnableJobs == null) {
1784                    runnableJobs = new ArrayList<>();
1785                }
1786                runnableJobs.add(job);
1787            }
1788        }
1789
1790        public void postProcess() {
1791            if (backoffCount > 0 ||
1792                    idleCount >= mConstants.MIN_IDLE_COUNT ||
1793                    connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1794                    chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1795                    batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1796                    storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1797                    contentCount >= mConstants.MIN_CONTENT_COUNT ||
1798                    (runnableJobs != null
1799                            && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1800                if (DEBUG) {
1801                    Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1802                }
1803                noteJobsPending(runnableJobs);
1804                mPendingJobs.addAll(runnableJobs);
1805                if (mPendingJobs.size() > 1) {
1806                    mPendingJobs.sort(mEnqueueTimeComparator);
1807                }
1808            } else {
1809                if (DEBUG) {
1810                    Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1811                }
1812            }
1813
1814            // Be ready for next time
1815            reset();
1816        }
1817
1818        private void reset() {
1819            chargingCount = 0;
1820            idleCount =  0;
1821            backoffCount = 0;
1822            connectivityCount = 0;
1823            batteryNotLowCount = 0;
1824            storageNotLowCount = 0;
1825            contentCount = 0;
1826            runnableJobs = null;
1827        }
1828    }
1829    private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1830
1831    private void maybeQueueReadyJobsForExecutionLocked() {
1832        if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1833
1834        noteJobsNonpending(mPendingJobs);
1835        mPendingJobs.clear();
1836        stopNonReadyActiveJobsLocked();
1837        mJobs.forEachJob(mMaybeQueueFunctor);
1838        mMaybeQueueFunctor.postProcess();
1839    }
1840
1841    /**
1842     * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
1843     */
1844    class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1845
1846        @Override
1847        public void onAlarm() {
1848            synchronized (mLock) {
1849                final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1850                final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1851                if (beatsElapsed > 0) {
1852                    mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1853                    advanceHeartbeatLocked(beatsElapsed);
1854                }
1855            }
1856            setNextHeartbeatAlarm();
1857        }
1858    }
1859
1860    // Intentionally does not touch the alarm timing
1861    void advanceHeartbeatLocked(long beatsElapsed) {
1862        mHeartbeat += beatsElapsed;
1863        if (DEBUG_STANDBY) {
1864            Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1865                    + " to " + mHeartbeat);
1866        }
1867        // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
1868        // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1869        // new jobs scheduled by apps in that bucket will be permitted to run
1870        // immediately.
1871        boolean didAdvanceBucket = false;
1872        for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
1873            // Did we reach or cross a bucket boundary?
1874            if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1875                didAdvanceBucket = true;
1876            }
1877            while (mHeartbeat > mNextBucketHeartbeat[i]) {
1878                mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1879            }
1880            if (DEBUG_STANDBY) {
1881                Slog.v(TAG, "   Bucket " + i + " next heartbeat "
1882                        + mNextBucketHeartbeat[i]);
1883            }
1884        }
1885
1886        if (didAdvanceBucket) {
1887            if (DEBUG_STANDBY) {
1888                Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1889            }
1890            mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1891        }
1892    }
1893
1894    void setNextHeartbeatAlarm() {
1895        final long heartbeatLength;
1896        synchronized (mLock) {
1897            heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1898        }
1899        final long now = sElapsedRealtimeClock.millis();
1900        final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1901        final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1902        if (DEBUG_STANDBY) {
1903            Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1904                    + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1905        }
1906        AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1907        am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1908                HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
1909    }
1910
1911    /**
1912     * Criteria for moving a job into the pending queue:
1913     *      - It's ready.
1914     *      - It's not pending.
1915     *      - It's not already running on a JSC.
1916     *      - The user that requested the job is running.
1917     *      - The job's standby bucket has come due to be runnable.
1918     *      - The component is enabled and runnable.
1919     */
1920    private boolean isReadyToBeExecutedLocked(JobStatus job) {
1921        final boolean jobReady = job.isReady();
1922
1923        if (DEBUG) {
1924            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1925                    + " ready=" + jobReady);
1926        }
1927
1928        // This is a condition that is very likely to be false (most jobs that are
1929        // scheduled are sitting there, not ready yet) and very cheap to check (just
1930        // a few conditions on data in JobStatus).
1931        if (!jobReady) {
1932            if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
1933                Slog.v(TAG, "    NOT READY: " + job);
1934            }
1935            return false;
1936        }
1937
1938        final boolean jobExists = mJobs.containsJob(job);
1939
1940        final int userId = job.getUserId();
1941        final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1942
1943        if (DEBUG) {
1944            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1945                    + " exists=" + jobExists + " userStarted=" + userStarted);
1946        }
1947
1948        // These are also fairly cheap to check, though they typically will not
1949        // be conditions we fail.
1950        if (!jobExists || !userStarted) {
1951            return false;
1952        }
1953
1954        final boolean jobPending = mPendingJobs.contains(job);
1955        final boolean jobActive = isCurrentlyActiveLocked(job);
1956
1957        if (DEBUG) {
1958            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1959                    + " pending=" + jobPending + " active=" + jobActive);
1960        }
1961
1962        // These can be a little more expensive (especially jobActive, since we need to
1963        // go through the array of all potentially active jobs), so we are doing them
1964        // later...  but still before checking with the package manager!
1965        if (jobPending || jobActive) {
1966            return false;
1967        }
1968
1969        // If the app is in a non-active standby bucket, make sure we've waited
1970        // an appropriate amount of time since the last invocation.  During device-
1971        // wide parole, standby bucketing is ignored.
1972        //
1973        // Jobs in 'active' apps are not subject to standby, nor are jobs that are
1974        // specifically marked as exempt.
1975        if (DEBUG_STANDBY) {
1976            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1977                    + " parole=" + mInParole + " active=" + job.uidActive
1978                    + " exempt=" + job.getJob().isExemptedFromAppStandby());
1979        }
1980        if (!mInParole
1981                && !job.uidActive
1982                && !job.getJob().isExemptedFromAppStandby()) {
1983            final int bucket = job.getStandbyBucket();
1984            if (DEBUG_STANDBY) {
1985                Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
1986                        + " next=" + mNextBucketHeartbeat[bucket]);
1987            }
1988            if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
1989                // Only skip this job if the app is still waiting for the end of its nominal
1990                // bucket interval.  Once it's waited that long, we let it go ahead and clear.
1991                // The final (NEVER) bucket is special; we never age those apps' jobs into
1992                // runnability.
1993                final long appLastRan = heartbeatWhenJobsLastRun(job);
1994                if (bucket >= mConstants.STANDBY_BEATS.length
1995                        || (mHeartbeat > appLastRan
1996                                && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
1997                    // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1998                    if (job.getWhenStandbyDeferred() == 0) {
1999                        if (DEBUG_STANDBY) {
2000                            Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
2001                                    + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2002                                    + " for " + job);
2003                        }
2004                        job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
2005                    }
2006                    return false;
2007                } else {
2008                    if (DEBUG_STANDBY) {
2009                        Slog.v(TAG, "Bucket deferred job aged into runnability at "
2010                                + mHeartbeat + " : " + job);
2011                    }
2012                }
2013            }
2014        }
2015
2016        // The expensive check last: validate that the defined package+service is
2017        // still present & viable.
2018        final boolean componentPresent;
2019        try {
2020            componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2021                    job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2022                    userId) != null);
2023        } catch (RemoteException e) {
2024            throw e.rethrowAsRuntimeException();
2025        }
2026
2027        if (DEBUG) {
2028            Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2029                    + " componentPresent=" + componentPresent);
2030        }
2031
2032        // Everything else checked out so far, so this is the final yes/no check
2033        return componentPresent;
2034    }
2035
2036    /**
2037     * Reconcile jobs in the pending queue against available execution contexts.
2038     * A controller can force a job into the pending queue even if it's already running, but
2039     * here is where we decide whether to actually execute it.
2040     */
2041    private void maybeRunPendingJobsLocked() {
2042        if (DEBUG) {
2043            Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2044        }
2045        assignJobsToContextsLocked();
2046        reportActiveLocked();
2047    }
2048
2049    private int adjustJobPriority(int curPriority, JobStatus job) {
2050        if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2051            float factor = mJobPackageTracker.getLoadFactor(job);
2052            if (factor >= mConstants.HEAVY_USE_FACTOR) {
2053                curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
2054            } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
2055                curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2056            }
2057        }
2058        return curPriority;
2059    }
2060
2061    private int evaluateJobPriorityLocked(JobStatus job) {
2062        int priority = job.getPriority();
2063        if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
2064            return adjustJobPriority(priority, job);
2065        }
2066        int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2067        if (override != 0) {
2068            return adjustJobPriority(override, job);
2069        }
2070        return adjustJobPriority(priority, job);
2071    }
2072
2073    /**
2074     * Takes jobs from pending queue and runs them on available contexts.
2075     * If no contexts are available, preempts lower priority jobs to
2076     * run higher priority ones.
2077     * Lock on mJobs before calling this function.
2078     */
2079    private void assignJobsToContextsLocked() {
2080        if (DEBUG) {
2081            Slog.d(TAG, printPendingQueue());
2082        }
2083
2084        int memLevel;
2085        try {
2086            memLevel = ActivityManager.getService().getMemoryTrimLevel();
2087        } catch (RemoteException e) {
2088            memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
2089        }
2090        switch (memLevel) {
2091            case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
2092                mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
2093                break;
2094            case ProcessStats.ADJ_MEM_FACTOR_LOW:
2095                mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
2096                break;
2097            case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
2098                mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
2099                break;
2100            default:
2101                mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
2102                break;
2103        }
2104
2105        JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
2106        boolean[] act = mTmpAssignAct;
2107        int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2108        int numActive = 0;
2109        int numForeground = 0;
2110        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2111            final JobServiceContext js = mActiveServices.get(i);
2112            final JobStatus status = js.getRunningJobLocked();
2113            if ((contextIdToJobMap[i] = status) != null) {
2114                numActive++;
2115                if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2116                    numForeground++;
2117                }
2118            }
2119            act[i] = false;
2120            preferredUidForContext[i] = js.getPreferredUid();
2121        }
2122        if (DEBUG) {
2123            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2124        }
2125        for (int i=0; i<mPendingJobs.size(); i++) {
2126            JobStatus nextPending = mPendingJobs.get(i);
2127
2128            // If job is already running, go to next job.
2129            int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2130            if (jobRunningContext != -1) {
2131                continue;
2132            }
2133
2134            final int priority = evaluateJobPriorityLocked(nextPending);
2135            nextPending.lastEvaluatedPriority = priority;
2136
2137            // Find a context for nextPending. The context should be available OR
2138            // it should have lowest priority among all running jobs
2139            // (sharing the same Uid as nextPending)
2140            int minPriority = Integer.MAX_VALUE;
2141            int minPriorityContextId = -1;
2142            for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2143                JobStatus job = contextIdToJobMap[j];
2144                int preferredUid = preferredUidForContext[j];
2145                if (job == null) {
2146                    if ((numActive < mMaxActiveJobs ||
2147                            (priority >= JobInfo.PRIORITY_TOP_APP &&
2148                                    numForeground < mConstants.FG_JOB_COUNT)) &&
2149                            (preferredUid == nextPending.getUid() ||
2150                                    preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2151                        // This slot is free, and we haven't yet hit the limit on
2152                        // concurrent jobs...  we can just throw the job in to here.
2153                        minPriorityContextId = j;
2154                        break;
2155                    }
2156                    // No job on this context, but nextPending can't run here because
2157                    // the context has a preferred Uid or we have reached the limit on
2158                    // concurrent jobs.
2159                    continue;
2160                }
2161                if (job.getUid() != nextPending.getUid()) {
2162                    continue;
2163                }
2164                if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
2165                    continue;
2166                }
2167                if (minPriority > nextPending.lastEvaluatedPriority) {
2168                    minPriority = nextPending.lastEvaluatedPriority;
2169                    minPriorityContextId = j;
2170                }
2171            }
2172            if (minPriorityContextId != -1) {
2173                contextIdToJobMap[minPriorityContextId] = nextPending;
2174                act[minPriorityContextId] = true;
2175                numActive++;
2176                if (priority >= JobInfo.PRIORITY_TOP_APP) {
2177                    numForeground++;
2178                }
2179            }
2180        }
2181        if (DEBUG) {
2182            Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2183        }
2184        mJobPackageTracker.noteConcurrency(numActive, numForeground);
2185        for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2186            boolean preservePreferredUid = false;
2187            if (act[i]) {
2188                JobStatus js = mActiveServices.get(i).getRunningJobLocked();
2189                if (js != null) {
2190                    if (DEBUG) {
2191                        Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
2192                    }
2193                    // preferredUid will be set to uid of currently running job.
2194                    mActiveServices.get(i).preemptExecutingJobLocked();
2195                    preservePreferredUid = true;
2196                } else {
2197                    final JobStatus pendingJob = contextIdToJobMap[i];
2198                    if (DEBUG) {
2199                        Slog.d(TAG, "About to run job on context "
2200                                + String.valueOf(i) + ", job: " + pendingJob);
2201                    }
2202                    for (int ic=0; ic<mControllers.size(); ic++) {
2203                        mControllers.get(ic).prepareForExecutionLocked(pendingJob);
2204                    }
2205                    if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2206                        Slog.d(TAG, "Error executing " + pendingJob);
2207                    }
2208                    if (mPendingJobs.remove(pendingJob)) {
2209                        mJobPackageTracker.noteNonpending(pendingJob);
2210                    }
2211                }
2212            }
2213            if (!preservePreferredUid) {
2214                mActiveServices.get(i).clearPreferredUid();
2215            }
2216        }
2217    }
2218
2219    int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2220        for (int i=0; i<map.length; i++) {
2221            if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2222                return i;
2223            }
2224        }
2225        return -1;
2226    }
2227
2228    final class LocalService implements JobSchedulerInternal {
2229
2230        /**
2231         * The current bucket heartbeat ordinal
2232         */
2233        public long currentHeartbeat() {
2234            return getCurrentHeartbeat();
2235        }
2236
2237        /**
2238         * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2239         */
2240        public long nextHeartbeatForBucket(int bucket) {
2241            synchronized (mLock) {
2242                return mNextBucketHeartbeat[bucket];
2243            }
2244        }
2245
2246        /**
2247         * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
2248         * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2249         * jobs in a long time is immediately runnable even if the app is bucketed into
2250         * an infrequent time allocation.
2251         */
2252        public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2253                final int appStandbyBucket) {
2254            if (appStandbyBucket == 0 ||
2255                    appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2256                // ACTIVE => everything can be run right away
2257                // NEVER => we won't run them anyway, so let them go in the future
2258                // as soon as the app enters normal use
2259                if (DEBUG_STANDBY) {
2260                    Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2261                            + packageName + "/" + userId);
2262                }
2263                return 0;
2264            }
2265
2266            final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2267            if (DEBUG_STANDBY) {
2268                Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2269                        + packageName + "/" + userId);
2270            }
2271            return baseHeartbeat;
2272        }
2273
2274        public void noteJobStart(String packageName, int userId) {
2275            synchronized (mLock) {
2276                setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2277            }
2278        }
2279
2280        /**
2281         * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2282         * jobs are always considered pending.
2283         */
2284        @Override
2285        public List<JobInfo> getSystemScheduledPendingJobs() {
2286            synchronized (mLock) {
2287                final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
2288                mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2289                    if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2290                        pendingJobs.add(job.getJob());
2291                    }
2292                });
2293                return pendingJobs;
2294            }
2295        }
2296
2297        @Override
2298        public void cancelJobsForUid(int uid, String reason) {
2299            JobSchedulerService.this.cancelJobsForUid(uid, reason);
2300        }
2301
2302        @Override
2303        public void addBackingUpUid(int uid) {
2304            synchronized (mLock) {
2305                // No need to actually do anything here, since for a full backup the
2306                // activity manager will kill the process which will kill the job (and
2307                // cause it to restart, but now it can't run).
2308                mBackingUpUids.put(uid, uid);
2309            }
2310        }
2311
2312        @Override
2313        public void removeBackingUpUid(int uid) {
2314            synchronized (mLock) {
2315                mBackingUpUids.delete(uid);
2316                // If there are any jobs for this uid, we need to rebuild the pending list
2317                // in case they are now ready to run.
2318                if (mJobs.countJobsForUid(uid) > 0) {
2319                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2320                }
2321            }
2322        }
2323
2324        @Override
2325        public void clearAllBackingUpUids() {
2326            synchronized (mLock) {
2327                if (mBackingUpUids.size() > 0) {
2328                    mBackingUpUids.clear();
2329                    mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2330                }
2331            }
2332        }
2333
2334        @Override
2335        public void reportAppUsage(String packageName, int userId) {
2336            JobSchedulerService.this.reportAppUsage(packageName, userId);
2337        }
2338
2339        @Override
2340        public JobStorePersistStats getPersistStats() {
2341            synchronized (mLock) {
2342                return new JobStorePersistStats(mJobs.getPersistStats());
2343            }
2344        }
2345    }
2346
2347    /**
2348     * Tracking of app assignments to standby buckets
2349     */
2350    final class StandbyTracker extends AppIdleStateChangeListener {
2351
2352        // AppIdleStateChangeListener interface for live updates
2353
2354        @Override
2355        public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
2356                boolean idle, int bucket, int reason) {
2357            final int uid = mLocalPM.getPackageUid(packageName,
2358                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2359            if (uid < 0) {
2360                if (DEBUG_STANDBY) {
2361                    Slog.i(TAG, "App idle state change for unknown app "
2362                            + packageName + "/" + userId);
2363                }
2364                return;
2365            }
2366
2367            final int bucketIndex = standbyBucketToBucketIndex(bucket);
2368            // update job bookkeeping out of band
2369            BackgroundThread.getHandler().post(() -> {
2370                if (DEBUG_STANDBY) {
2371                    Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2372                }
2373                synchronized (mLock) {
2374                    mJobs.forEachJobForSourceUid(uid, job -> {
2375                        // double-check uid vs package name to disambiguate shared uids
2376                        if (packageName.equals(job.getSourcePackageName())) {
2377                            job.setStandbyBucket(bucketIndex);
2378                        }
2379                    });
2380                    onControllerStateChanged();
2381                }
2382            });
2383        }
2384
2385        @Override
2386        public void onParoleStateChanged(boolean isParoleOn) {
2387            if (DEBUG_STANDBY) {
2388                Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2389            }
2390            mInParole = isParoleOn;
2391        }
2392
2393        @Override
2394        public void onUserInteractionStarted(String packageName, int userId) {
2395            final int uid = mLocalPM.getPackageUid(packageName,
2396                    PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2397            if (uid < 0) {
2398                // Quietly ignore; the case is already logged elsewhere
2399                return;
2400            }
2401
2402            long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2403            if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2404                // Too long ago, not worth logging
2405                sinceLast = 0L;
2406            }
2407            final DeferredJobCounter counter = new DeferredJobCounter();
2408            synchronized (mLock) {
2409                mJobs.forEachJobForSourceUid(uid, counter);
2410            }
2411            if (counter.numDeferred() > 0 || sinceLast > 0) {
2412                BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2413                        (BatteryStatsInternal.class);
2414                mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2415            }
2416        }
2417    }
2418
2419    static class DeferredJobCounter implements Consumer<JobStatus> {
2420        private int mDeferred = 0;
2421
2422        public int numDeferred() {
2423            return mDeferred;
2424        }
2425
2426        @Override
2427        public void accept(JobStatus job) {
2428            if (job.getWhenStandbyDeferred() > 0) {
2429                mDeferred++;
2430            }
2431        }
2432    }
2433
2434    public static int standbyBucketToBucketIndex(int bucket) {
2435        // Normalize AppStandby constants to indices into our bookkeeping
2436        if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2437        else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2438        else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2439        else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2440        else return ACTIVE_INDEX;
2441    }
2442
2443    // Static to support external callers
2444    public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2445        UsageStatsManagerInternal usageStats = LocalServices.getService(
2446                UsageStatsManagerInternal.class);
2447        int bucket = usageStats != null
2448                ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2449                : 0;
2450
2451        bucket = standbyBucketToBucketIndex(bucket);
2452
2453        if (DEBUG_STANDBY) {
2454            Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2455        }
2456        return bucket;
2457    }
2458
2459    /**
2460     * Binder stub trampoline implementation
2461     */
2462    final class JobSchedulerStub extends IJobScheduler.Stub {
2463        /** Cache determination of whether a given app can persist jobs
2464         * key is uid of the calling app; value is undetermined/true/false
2465         */
2466        private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2467
2468        // Enforce that only the app itself (or shared uid participant) can schedule a
2469        // job that runs one of the app's services, as well as verifying that the
2470        // named service properly requires the BIND_JOB_SERVICE permission
2471        private void enforceValidJobRequest(int uid, JobInfo job) {
2472            final IPackageManager pm = AppGlobals.getPackageManager();
2473            final ComponentName service = job.getService();
2474            try {
2475                ServiceInfo si = pm.getServiceInfo(service,
2476                        PackageManager.MATCH_DIRECT_BOOT_AWARE
2477                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
2478                        UserHandle.getUserId(uid));
2479                if (si == null) {
2480                    throw new IllegalArgumentException("No such service " + service);
2481                }
2482                if (si.applicationInfo.uid != uid) {
2483                    throw new IllegalArgumentException("uid " + uid +
2484                            " cannot schedule job in " + service.getPackageName());
2485                }
2486                if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2487                    throw new IllegalArgumentException("Scheduled service " + service
2488                            + " does not require android.permission.BIND_JOB_SERVICE permission");
2489                }
2490            } catch (RemoteException e) {
2491                // Can't happen; the Package Manager is in this same process
2492            }
2493        }
2494
2495        private boolean canPersistJobs(int pid, int uid) {
2496            // If we get this far we're good to go; all we need to do now is check
2497            // whether the app is allowed to persist its scheduled work.
2498            final boolean canPersist;
2499            synchronized (mPersistCache) {
2500                Boolean cached = mPersistCache.get(uid);
2501                if (cached != null) {
2502                    canPersist = cached.booleanValue();
2503                } else {
2504                    // Persisting jobs is tantamount to running at boot, so we permit
2505                    // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2506                    // permission
2507                    int result = getContext().checkPermission(
2508                            android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2509                    canPersist = (result == PackageManager.PERMISSION_GRANTED);
2510                    mPersistCache.put(uid, canPersist);
2511                }
2512            }
2513            return canPersist;
2514        }
2515
2516        private void validateJobFlags(JobInfo job, int callingUid) {
2517            if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2518                getContext().enforceCallingOrSelfPermission(
2519                        android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2520            }
2521            if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2522                if (callingUid != Process.SYSTEM_UID) {
2523                    throw new SecurityException("Job has invalid flags");
2524                }
2525                if (job.isPeriodic()) {
2526                    Slog.wtf(TAG, "Periodic jobs mustn't have"
2527                            + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
2528                }
2529            }
2530        }
2531
2532        // IJobScheduler implementation
2533        @Override
2534        public int schedule(JobInfo job) throws RemoteException {
2535            if (DEBUG) {
2536                Slog.d(TAG, "Scheduling job: " + job.toString());
2537            }
2538            final int pid = Binder.getCallingPid();
2539            final int uid = Binder.getCallingUid();
2540            final int userId = UserHandle.getUserId(uid);
2541
2542            enforceValidJobRequest(uid, job);
2543            if (job.isPersisted()) {
2544                if (!canPersistJobs(pid, uid)) {
2545                    throw new IllegalArgumentException("Error: requested job be persisted without"
2546                            + " holding RECEIVE_BOOT_COMPLETED permission.");
2547                }
2548            }
2549
2550            validateJobFlags(job, uid);
2551
2552            long ident = Binder.clearCallingIdentity();
2553            try {
2554                return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2555                        null);
2556            } finally {
2557                Binder.restoreCallingIdentity(ident);
2558            }
2559        }
2560
2561        // IJobScheduler implementation
2562        @Override
2563        public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2564            if (DEBUG) {
2565                Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2566            }
2567            final int uid = Binder.getCallingUid();
2568            final int userId = UserHandle.getUserId(uid);
2569
2570            enforceValidJobRequest(uid, job);
2571            if (job.isPersisted()) {
2572                throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2573            }
2574            if (work == null) {
2575                throw new NullPointerException("work is null");
2576            }
2577
2578            validateJobFlags(job, uid);
2579
2580            long ident = Binder.clearCallingIdentity();
2581            try {
2582                return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2583                        null);
2584            } finally {
2585                Binder.restoreCallingIdentity(ident);
2586            }
2587        }
2588
2589        @Override
2590        public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
2591                throws RemoteException {
2592            final int callerUid = Binder.getCallingUid();
2593            if (DEBUG) {
2594                Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
2595                        + " on behalf of " + packageName + "/");
2596            }
2597
2598            if (packageName == null) {
2599                throw new NullPointerException("Must specify a package for scheduleAsPackage()");
2600            }
2601
2602            int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2603                    android.Manifest.permission.UPDATE_DEVICE_STATS);
2604            if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2605                throw new SecurityException("Caller uid " + callerUid
2606                        + " not permitted to schedule jobs for other apps");
2607            }
2608
2609            validateJobFlags(job, callerUid);
2610
2611            long ident = Binder.clearCallingIdentity();
2612            try {
2613                return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
2614                        packageName, userId, tag);
2615            } finally {
2616                Binder.restoreCallingIdentity(ident);
2617            }
2618        }
2619
2620        @Override
2621        public List<JobInfo> getAllPendingJobs() throws RemoteException {
2622            final int uid = Binder.getCallingUid();
2623
2624            long ident = Binder.clearCallingIdentity();
2625            try {
2626                return JobSchedulerService.this.getPendingJobs(uid);
2627            } finally {
2628                Binder.restoreCallingIdentity(ident);
2629            }
2630        }
2631
2632        @Override
2633        public JobInfo getPendingJob(int jobId) throws RemoteException {
2634            final int uid = Binder.getCallingUid();
2635
2636            long ident = Binder.clearCallingIdentity();
2637            try {
2638                return JobSchedulerService.this.getPendingJob(uid, jobId);
2639            } finally {
2640                Binder.restoreCallingIdentity(ident);
2641            }
2642        }
2643
2644        @Override
2645        public void cancelAll() throws RemoteException {
2646            final int uid = Binder.getCallingUid();
2647            long ident = Binder.clearCallingIdentity();
2648            try {
2649                JobSchedulerService.this.cancelJobsForUid(uid,
2650                        "cancelAll() called by app, callingUid=" + uid);
2651            } finally {
2652                Binder.restoreCallingIdentity(ident);
2653            }
2654        }
2655
2656        @Override
2657        public void cancel(int jobId) throws RemoteException {
2658            final int uid = Binder.getCallingUid();
2659
2660            long ident = Binder.clearCallingIdentity();
2661            try {
2662                JobSchedulerService.this.cancelJob(uid, jobId, uid);
2663            } finally {
2664                Binder.restoreCallingIdentity(ident);
2665            }
2666        }
2667
2668        /**
2669         * "dumpsys" infrastructure
2670         */
2671        @Override
2672        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2673            if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2674
2675            int filterUid = -1;
2676            boolean proto = false;
2677            if (!ArrayUtils.isEmpty(args)) {
2678                int opti = 0;
2679                while (opti < args.length) {
2680                    String arg = args[opti];
2681                    if ("-h".equals(arg)) {
2682                        dumpHelp(pw);
2683                        return;
2684                    } else if ("-a".equals(arg)) {
2685                        // Ignore, we always dump all.
2686                    } else if ("--proto".equals(arg)) {
2687                        proto = true;
2688                    } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2689                        pw.println("Unknown option: " + arg);
2690                        return;
2691                    } else {
2692                        break;
2693                    }
2694                    opti++;
2695                }
2696                if (opti < args.length) {
2697                    String pkg = args[opti];
2698                    try {
2699                        filterUid = getContext().getPackageManager().getPackageUid(pkg,
2700                                PackageManager.MATCH_ANY_USER);
2701                    } catch (NameNotFoundException ignored) {
2702                        pw.println("Invalid package: " + pkg);
2703                        return;
2704                    }
2705                }
2706            }
2707
2708            final long identityToken = Binder.clearCallingIdentity();
2709            try {
2710                if (proto) {
2711                    JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2712                } else {
2713                    JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, "  "),
2714                            filterUid);
2715                }
2716            } finally {
2717                Binder.restoreCallingIdentity(identityToken);
2718            }
2719        }
2720
2721        @Override
2722        public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2723                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
2724                (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2725                        this, in, out, err, args, callback, resultReceiver);
2726        }
2727    };
2728
2729    // Shell command infrastructure: run the given job immediately
2730    int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2731        if (DEBUG) {
2732            Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2733                    + " " + jobId + " f=" + force);
2734        }
2735
2736        try {
2737            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2738                    userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2739            if (uid < 0) {
2740                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2741            }
2742
2743            synchronized (mLock) {
2744                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2745                if (js == null) {
2746                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2747                }
2748
2749                js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2750                if (!js.isConstraintsSatisfied()) {
2751                    js.overrideState = 0;
2752                    return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2753                }
2754
2755                queueReadyJobsForExecutionLocked();
2756                maybeRunPendingJobsLocked();
2757            }
2758        } catch (RemoteException e) {
2759            // can't happen
2760        }
2761        return 0;
2762    }
2763
2764    // Shell command infrastructure: immediately timeout currently executing jobs
2765    int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2766            boolean hasJobId, int jobId) {
2767        if (DEBUG) {
2768            Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2769        }
2770
2771        synchronized (mLock) {
2772            boolean foundSome = false;
2773            for (int i=0; i<mActiveServices.size(); i++) {
2774                final JobServiceContext jc = mActiveServices.get(i);
2775                final JobStatus js = jc.getRunningJobLocked();
2776                if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
2777                    foundSome = true;
2778                    pw.print("Timing out: ");
2779                    js.printUniqueId(pw);
2780                    pw.print(" ");
2781                    pw.println(js.getServiceComponent().flattenToShortString());
2782                }
2783            }
2784            if (!foundSome) {
2785                pw.println("No matching executing jobs found.");
2786            }
2787        }
2788        return 0;
2789    }
2790
2791    // Shell command infrastructure: cancel a scheduled job
2792    int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2793            boolean hasJobId, int jobId) {
2794        if (DEBUG) {
2795            Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2796        }
2797
2798        int pkgUid = -1;
2799        try {
2800            IPackageManager pm = AppGlobals.getPackageManager();
2801            pkgUid = pm.getPackageUid(pkgName, 0, userId);
2802        } catch (RemoteException e) { /* can't happen */ }
2803
2804        if (pkgUid < 0) {
2805            pw.println("Package " + pkgName + " not found.");
2806            return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2807        }
2808
2809        if (!hasJobId) {
2810            pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2811            if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2812                pw.println("No matching jobs found.");
2813            }
2814        } else {
2815            pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
2816            if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
2817                pw.println("No matching job found.");
2818            }
2819        }
2820
2821        return 0;
2822    }
2823
2824    void setMonitorBattery(boolean enabled) {
2825        synchronized (mLock) {
2826            if (mBatteryController != null) {
2827                mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2828            }
2829        }
2830    }
2831
2832    int getBatterySeq() {
2833        synchronized (mLock) {
2834            return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2835        }
2836    }
2837
2838    boolean getBatteryCharging() {
2839        synchronized (mLock) {
2840            return mBatteryController != null
2841                    ? mBatteryController.getTracker().isOnStablePower() : false;
2842        }
2843    }
2844
2845    boolean getBatteryNotLow() {
2846        synchronized (mLock) {
2847            return mBatteryController != null
2848                    ? mBatteryController.getTracker().isBatteryNotLow() : false;
2849        }
2850    }
2851
2852    int getStorageSeq() {
2853        synchronized (mLock) {
2854            return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2855        }
2856    }
2857
2858    boolean getStorageNotLow() {
2859        synchronized (mLock) {
2860            return mStorageController != null
2861                    ? mStorageController.getTracker().isStorageNotLow() : false;
2862        }
2863    }
2864
2865    long getCurrentHeartbeat() {
2866        synchronized (mLock) {
2867            return mHeartbeat;
2868        }
2869    }
2870
2871    // Shell command infrastructure
2872    int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2873        try {
2874            final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2875                    userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2876            if (uid < 0) {
2877                pw.print("unknown("); pw.print(pkgName); pw.println(")");
2878                return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2879            }
2880
2881            synchronized (mLock) {
2882                final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2883                if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2884                if (js == null) {
2885                    pw.print("unknown("); UserHandle.formatUid(pw, uid);
2886                    pw.print("/jid"); pw.print(jobId); pw.println(")");
2887                    return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2888                }
2889
2890                boolean printed = false;
2891                if (mPendingJobs.contains(js)) {
2892                    pw.print("pending");
2893                    printed = true;
2894                }
2895                if (isCurrentlyActiveLocked(js)) {
2896                    if (printed) {
2897                        pw.print(" ");
2898                    }
2899                    printed = true;
2900                    pw.println("active");
2901                }
2902                if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2903                    if (printed) {
2904                        pw.print(" ");
2905                    }
2906                    printed = true;
2907                    pw.println("user-stopped");
2908                }
2909                if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2910                    if (printed) {
2911                        pw.print(" ");
2912                    }
2913                    printed = true;
2914                    pw.println("backing-up");
2915                }
2916                boolean componentPresent = false;
2917                try {
2918                    componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2919                            js.getServiceComponent(),
2920                            PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2921                            js.getUserId()) != null);
2922                } catch (RemoteException e) {
2923                }
2924                if (!componentPresent) {
2925                    if (printed) {
2926                        pw.print(" ");
2927                    }
2928                    printed = true;
2929                    pw.println("no-component");
2930                }
2931                if (js.isReady()) {
2932                    if (printed) {
2933                        pw.print(" ");
2934                    }
2935                    printed = true;
2936                    pw.println("ready");
2937                }
2938                if (!printed) {
2939                    pw.print("waiting");
2940                }
2941                pw.println();
2942            }
2943        } catch (RemoteException e) {
2944            // can't happen
2945        }
2946        return 0;
2947    }
2948
2949    // Shell command infrastructure
2950    int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
2951        if (numBeats < 1) {
2952            pw.println(getCurrentHeartbeat());
2953            return 0;
2954        }
2955
2956        pw.print("Advancing standby heartbeat by ");
2957        pw.println(numBeats);
2958        synchronized (mLock) {
2959            advanceHeartbeatLocked(numBeats);
2960        }
2961        return 0;
2962    }
2963
2964    void triggerDockState(boolean idleState) {
2965        final Intent dockIntent;
2966        if (idleState) {
2967            dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
2968        } else {
2969            dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
2970        }
2971        dockIntent.setPackage("android");
2972        dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
2973        getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
2974    }
2975
2976    private String printContextIdToJobMap(JobStatus[] map, String initial) {
2977        StringBuilder s = new StringBuilder(initial + ": ");
2978        for (int i=0; i<map.length; i++) {
2979            s.append("(")
2980                    .append(map[i] == null? -1: map[i].getJobId())
2981                    .append(map[i] == null? -1: map[i].getUid())
2982                    .append(")" );
2983        }
2984        return s.toString();
2985    }
2986
2987    private String printPendingQueue() {
2988        StringBuilder s = new StringBuilder("Pending queue: ");
2989        Iterator<JobStatus> it = mPendingJobs.iterator();
2990        while (it.hasNext()) {
2991            JobStatus js = it.next();
2992            s.append("(")
2993                    .append(js.getJob().getId())
2994                    .append(", ")
2995                    .append(js.getUid())
2996                    .append(") ");
2997        }
2998        return s.toString();
2999    }
3000
3001    static void dumpHelp(PrintWriter pw) {
3002        pw.println("Job Scheduler (jobscheduler) dump options:");
3003        pw.println("  [-h] [package] ...");
3004        pw.println("    -h: print this help");
3005        pw.println("  [package] is an optional package name to limit the output to.");
3006    }
3007
3008    /** Sort jobs by caller UID, then by Job ID. */
3009    private static void sortJobs(List<JobStatus> jobs) {
3010        Collections.sort(jobs, new Comparator<JobStatus>() {
3011            @Override
3012            public int compare(JobStatus o1, JobStatus o2) {
3013                int uid1 = o1.getUid();
3014                int uid2 = o2.getUid();
3015                int id1 = o1.getJobId();
3016                int id2 = o2.getJobId();
3017                if (uid1 != uid2) {
3018                    return uid1 < uid2 ? -1 : 1;
3019                }
3020                return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
3021            }
3022        });
3023    }
3024
3025    void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
3026        final int filterUidFinal = UserHandle.getAppId(filterUid);
3027        final long nowElapsed = sElapsedRealtimeClock.millis();
3028        final long nowUptime = sUptimeMillisClock.millis();
3029        final Predicate<JobStatus> predicate = (js) -> {
3030            return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3031                    || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3032        };
3033        synchronized (mLock) {
3034            mConstants.dump(pw);
3035            pw.println();
3036
3037            pw.println("  Heartbeat:");
3038            pw.print("    Current:    "); pw.println(mHeartbeat);
3039            pw.println("    Next");
3040            pw.print("      ACTIVE:   "); pw.println(mNextBucketHeartbeat[0]);
3041            pw.print("      WORKING:  "); pw.println(mNextBucketHeartbeat[1]);
3042            pw.print("      FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3043            pw.print("      RARE:     "); pw.println(mNextBucketHeartbeat[3]);
3044            pw.print("    Last heartbeat: ");
3045            TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3046            pw.println();
3047            pw.print("    Next heartbeat: ");
3048            TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3049                    nowElapsed, pw);
3050            pw.println();
3051            pw.print("    In parole?: ");
3052            pw.print(mInParole);
3053            pw.println();
3054            pw.println();
3055
3056            pw.println("Started users: " + Arrays.toString(mStartedUsers));
3057            pw.print("Registered ");
3058            pw.print(mJobs.size());
3059            pw.println(" jobs:");
3060            if (mJobs.size() > 0) {
3061                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3062                sortJobs(jobs);
3063                for (JobStatus job : jobs) {
3064                    pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
3065                    pw.println(job.toShortStringExceptUniqueId());
3066
3067                    // Skip printing details if the caller requested a filter
3068                    if (!predicate.test(job)) {
3069                        continue;
3070                    }
3071
3072                    job.dump(pw, "    ", true, nowElapsed);
3073                    pw.print("    Last run heartbeat: ");
3074                    pw.print(heartbeatWhenJobsLastRun(job));
3075                    pw.println();
3076
3077                    pw.print("    Ready: ");
3078                    pw.print(isReadyToBeExecutedLocked(job));
3079                    pw.print(" (job=");
3080                    pw.print(job.isReady());
3081                    pw.print(" user=");
3082                    pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
3083                    pw.print(" !pending=");
3084                    pw.print(!mPendingJobs.contains(job));
3085                    pw.print(" !active=");
3086                    pw.print(!isCurrentlyActiveLocked(job));
3087                    pw.print(" !backingup=");
3088                    pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
3089                    pw.print(" comp=");
3090                    boolean componentPresent = false;
3091                    try {
3092                        componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3093                                job.getServiceComponent(),
3094                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3095                                job.getUserId()) != null);
3096                    } catch (RemoteException e) {
3097                    }
3098                    pw.print(componentPresent);
3099                    pw.println(")");
3100                }
3101            } else {
3102                pw.println("  None.");
3103            }
3104            for (int i=0; i<mControllers.size(); i++) {
3105                pw.println();
3106                pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3107                pw.increaseIndent();
3108                mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3109                pw.decreaseIndent();
3110            }
3111            pw.println();
3112            pw.println("Uid priority overrides:");
3113            for (int i=0; i< mUidPriorityOverride.size(); i++) {
3114                int uid = mUidPriorityOverride.keyAt(i);
3115                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3116                    pw.print("  "); pw.print(UserHandle.formatUid(uid));
3117                    pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3118                }
3119            }
3120            if (mBackingUpUids.size() > 0) {
3121                pw.println();
3122                pw.println("Backing up uids:");
3123                boolean first = true;
3124                for (int i = 0; i < mBackingUpUids.size(); i++) {
3125                    int uid = mBackingUpUids.keyAt(i);
3126                    if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3127                        if (first) {
3128                            pw.print("  ");
3129                            first = false;
3130                        } else {
3131                            pw.print(", ");
3132                        }
3133                        pw.print(UserHandle.formatUid(uid));
3134                    }
3135                }
3136                pw.println();
3137            }
3138            pw.println();
3139            mJobPackageTracker.dump(pw, "", filterUidFinal);
3140            pw.println();
3141            if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3142                pw.println();
3143            }
3144            pw.println("Pending queue:");
3145            for (int i=0; i<mPendingJobs.size(); i++) {
3146                JobStatus job = mPendingJobs.get(i);
3147                pw.print("  Pending #"); pw.print(i); pw.print(": ");
3148                pw.println(job.toShortString());
3149                job.dump(pw, "    ", false, nowElapsed);
3150                int priority = evaluateJobPriorityLocked(job);
3151                if (priority != JobInfo.PRIORITY_DEFAULT) {
3152                    pw.print("    Evaluated priority: "); pw.println(priority);
3153                }
3154                pw.print("    Tag: "); pw.println(job.getTag());
3155                pw.print("    Enq: ");
3156                TimeUtils.formatDuration(job.madePending - nowUptime, pw);
3157                pw.println();
3158            }
3159            pw.println();
3160            pw.println("Active jobs:");
3161            for (int i=0; i<mActiveServices.size(); i++) {
3162                JobServiceContext jsc = mActiveServices.get(i);
3163                pw.print("  Slot #"); pw.print(i); pw.print(": ");
3164                final JobStatus job = jsc.getRunningJobLocked();
3165                if (job == null) {
3166                    if (jsc.mStoppedReason != null) {
3167                        pw.print("inactive since ");
3168                        TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3169                        pw.print(", stopped because: ");
3170                        pw.println(jsc.mStoppedReason);
3171                    } else {
3172                        pw.println("inactive");
3173                    }
3174                    continue;
3175                } else {
3176                    pw.println(job.toShortString());
3177                    pw.print("    Running for: ");
3178                    TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
3179                    pw.print(", timeout at: ");
3180                    TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
3181                    pw.println();
3182                    job.dump(pw, "    ", false, nowElapsed);
3183                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3184                    if (priority != JobInfo.PRIORITY_DEFAULT) {
3185                        pw.print("    Evaluated priority: "); pw.println(priority);
3186                    }
3187                    pw.print("    Active at ");
3188                    TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
3189                    pw.print(", pending for ");
3190                    TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3191                    pw.println();
3192                }
3193            }
3194            if (filterUid == -1) {
3195                pw.println();
3196                pw.print("mReadyToRock="); pw.println(mReadyToRock);
3197                pw.print("mReportedActive="); pw.println(mReportedActive);
3198                pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3199            }
3200            pw.println();
3201            pw.print("PersistStats: ");
3202            pw.println(mJobs.getPersistStats());
3203        }
3204        pw.println();
3205    }
3206
3207    void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3208        ProtoOutputStream proto = new ProtoOutputStream(fd);
3209        final int filterUidFinal = UserHandle.getAppId(filterUid);
3210        final long nowElapsed = sElapsedRealtimeClock.millis();
3211        final long nowUptime = sUptimeMillisClock.millis();
3212        final Predicate<JobStatus> predicate = (js) -> {
3213            return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3214                    || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3215        };
3216
3217        synchronized (mLock) {
3218            mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
3219            proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3220            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3221            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3222            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3223            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3224            proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3225                    mLastHeartbeatTime - nowUptime);
3226            proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3227                    mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
3228            proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
3229
3230            for (int u : mStartedUsers) {
3231                proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3232            }
3233            if (mJobs.size() > 0) {
3234                final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3235                sortJobs(jobs);
3236                for (JobStatus job : jobs) {
3237                    final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3238                    job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3239
3240                    // Skip printing details if the caller requested a filter
3241                    if (!predicate.test(job)) {
3242                        continue;
3243                    }
3244
3245                    job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3246
3247                    // isReadyToBeExecuted
3248                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3249                            job.isReady());
3250                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3251                            ArrayUtils.contains(mStartedUsers, job.getUserId()));
3252                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3253                            mPendingJobs.contains(job));
3254                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3255                            isCurrentlyActiveLocked(job));
3256                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3257                            mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3258                    boolean componentPresent = false;
3259                    try {
3260                        componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3261                                job.getServiceComponent(),
3262                                PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3263                                job.getUserId()) != null);
3264                    } catch (RemoteException e) {
3265                    }
3266                    proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3267                            componentPresent);
3268                    proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
3269
3270                    proto.end(rjToken);
3271                }
3272            }
3273            for (StateController controller : mControllers) {
3274                controller.dumpControllerStateLocked(
3275                        proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
3276            }
3277            for (int i=0; i< mUidPriorityOverride.size(); i++) {
3278                int uid = mUidPriorityOverride.keyAt(i);
3279                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3280                    long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3281                    proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3282                    proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3283                            mUidPriorityOverride.valueAt(i));
3284                    proto.end(pToken);
3285                }
3286            }
3287            for (int i = 0; i < mBackingUpUids.size(); i++) {
3288                int uid = mBackingUpUids.keyAt(i);
3289                if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3290                    proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3291                }
3292            }
3293
3294            mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3295                    filterUidFinal);
3296            mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3297                    filterUidFinal);
3298
3299            for (JobStatus job : mPendingJobs) {
3300                final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3301
3302                job.writeToShortProto(proto, PendingJob.INFO);
3303                job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3304                int priority = evaluateJobPriorityLocked(job);
3305                if (priority != JobInfo.PRIORITY_DEFAULT) {
3306                    proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3307                }
3308                proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3309
3310                proto.end(pjToken);
3311            }
3312            for (JobServiceContext jsc : mActiveServices) {
3313                final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3314                final JobStatus job = jsc.getRunningJobLocked();
3315
3316                if (job == null) {
3317                    final long ijToken = proto.start(ActiveJob.INACTIVE);
3318
3319                        proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3320                                nowElapsed - jsc.mStoppedTime);
3321                    if (jsc.mStoppedReason != null) {
3322                        proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3323                                jsc.mStoppedReason);
3324                    }
3325
3326                    proto.end(ijToken);
3327                } else {
3328                    final long rjToken = proto.start(ActiveJob.RUNNING);
3329
3330                    job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3331
3332                    proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3333                            nowElapsed - jsc.getExecutionStartTimeElapsed());
3334                    proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3335                            jsc.getTimeoutElapsed() - nowElapsed);
3336
3337                    job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3338
3339                    int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3340                    if (priority != JobInfo.PRIORITY_DEFAULT) {
3341                        proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3342                    }
3343
3344                    proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3345                            nowUptime - job.madeActive);
3346                    proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3347                            job.madeActive - job.madePending);
3348
3349                    proto.end(rjToken);
3350                }
3351                proto.end(ajToken);
3352            }
3353            if (filterUid == -1) {
3354                proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3355                proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3356                proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3357            }
3358        }
3359
3360        proto.flush();
3361    }
3362}
3363