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