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