• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.job;
18 
19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
22 
23 import android.annotation.NonNull;
24 import android.annotation.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 onUserUnlocked(@NonNull TargetUser user) {
1012         synchronized (mLock) {
1013             // Note that the user has started after its unlocked instead of when the user
1014             // actually starts because the storage won't be decrypted until unlock.
1015             mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
1016         }
1017         // Let's kick any outstanding jobs for this user.
1018         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1019     }
1020 
1021     @Override
1022     public void onUserStopping(@NonNull TargetUser user) {
1023         synchronized (mLock) {
1024             mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
1025         }
1026     }
1027 
1028     /**
1029      * Return whether an UID is active or idle.
1030      */
1031     private boolean isUidActive(int uid) {
1032         return mAppStateTracker.isUidActiveSynced(uid);
1033     }
1034 
1035     private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
1036 
1037     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
1038             int userId, String tag) {
1039         // Rate limit excessive schedule() calls.
1040         final String servicePkg = job.getService().getPackageName();
1041         if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
1042             // Only limit schedule calls for persisted jobs scheduled by the app itself.
1043             final String pkg = packageName == null ? servicePkg : packageName;
1044             if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
1045                 if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
1046                     // Don't log too frequently
1047                     Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times");
1048                     mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED);
1049                 }
1050                 mAppStandbyInternal.restrictApp(
1051                         pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
1052                 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
1053                     final boolean isDebuggable;
1054                     synchronized (mLock) {
1055                         if (!mDebuggableApps.containsKey(packageName)) {
1056                             try {
1057                                 final ApplicationInfo appInfo = AppGlobals.getPackageManager()
1058                                         .getApplicationInfo(pkg, 0, userId);
1059                                 if (appInfo != null) {
1060                                     mDebuggableApps.put(packageName,
1061                                             (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
1062                                 } else {
1063                                     return JobScheduler.RESULT_FAILURE;
1064                                 }
1065                             } catch (RemoteException e) {
1066                                 throw new RuntimeException(e);
1067                             }
1068                         }
1069                         isDebuggable = mDebuggableApps.get(packageName);
1070                     }
1071                     if (isDebuggable) {
1072                         // Only throw the exception for debuggable apps.
1073                         throw new LimitExceededException(
1074                                 "schedule()/enqueue() called more than "
1075                                         + mQuotaTracker.getLimit(
1076                                         QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
1077                                         + " times in the past "
1078                                         + mQuotaTracker.getWindowSizeMs(
1079                                         QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
1080                                         + "ms. See the documentation for more information.");
1081                     }
1082                 }
1083                 if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) {
1084                     return JobScheduler.RESULT_FAILURE;
1085                 }
1086             }
1087             mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
1088         }
1089 
1090         if (mActivityManagerInternal.isAppStartModeDisabled(uId, servicePkg)) {
1091             Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1092                     + " -- package not allowed to start");
1093             return JobScheduler.RESULT_FAILURE;
1094         }
1095 
1096         synchronized (mLock) {
1097             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1098 
1099             if (work != null && toCancel != null) {
1100                 // Fast path: we are adding work to an existing job, and the JobInfo is not
1101                 // changing.  We can just directly enqueue this work in to the job.
1102                 if (toCancel.getJob().equals(job)) {
1103 
1104                     toCancel.enqueueWorkLocked(work);
1105 
1106                     // If any of work item is enqueued when the source is in the foreground,
1107                     // exempt the entire job.
1108                     toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
1109 
1110                     return JobScheduler.RESULT_SUCCESS;
1111                 }
1112             }
1113 
1114             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
1115 
1116             // Return failure early if expedited job quota used up.
1117             if (jobStatus.isRequestedExpeditedJob()
1118                     && !mQuotaController.isWithinEJQuotaLocked(jobStatus)) {
1119                 return JobScheduler.RESULT_FAILURE;
1120             }
1121 
1122             // Give exemption if the source is in the foreground just now.
1123             // Note if it's a sync job, this method is called on the handler so it's not exactly
1124             // the state when requestSync() was called, but that should be fine because of the
1125             // 1 minute foreground grace period.
1126             jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
1127 
1128             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
1129             // Jobs on behalf of others don't apply to the per-app job cap
1130             if (packageName == null) {
1131                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1132                     Slog.w(TAG, "Too many jobs for uid " + uId);
1133                     throw new IllegalStateException("Apps may not schedule more than "
1134                                 + MAX_JOBS_PER_APP + " distinct jobs");
1135                 }
1136             }
1137 
1138             // This may throw a SecurityException.
1139             jobStatus.prepareLocked();
1140 
1141             if (toCancel != null) {
1142                 // Implicitly replaces the existing job record with the new instance
1143                 cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
1144                         JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app");
1145             } else {
1146                 startTrackingJobLocked(jobStatus, null);
1147             }
1148 
1149             if (work != null) {
1150                 // If work has been supplied, enqueue it into the new job.
1151                 jobStatus.enqueueWorkLocked(work);
1152             }
1153 
1154             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
1155                     uId, null, jobStatus.getBatteryName(),
1156                     FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1157                     JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
1158                     jobStatus.getJobId(),
1159                     jobStatus.hasChargingConstraint(),
1160                     jobStatus.hasBatteryNotLowConstraint(),
1161                     jobStatus.hasStorageNotLowConstraint(),
1162                     jobStatus.hasTimingDelayConstraint(),
1163                     jobStatus.hasDeadlineConstraint(),
1164                     jobStatus.hasIdleConstraint(),
1165                     jobStatus.hasConnectivityConstraint(),
1166                     jobStatus.hasContentTriggerConstraint(),
1167                     jobStatus.isRequestedExpeditedJob(),
1168                     /* isRunningAsExpeditedJob */ false,
1169                     JobProtoEnums.STOP_REASON_UNDEFINED);
1170 
1171             // If the job is immediately ready to run, then we can just immediately
1172             // put it in the pending list and try to schedule it.  This is especially
1173             // important for jobs with a 0 deadline constraint, since they will happen a fair
1174             // amount, we want to handle them as quickly as possible, and semantically we want to
1175             // make sure we have started holding the wake lock for the job before returning to
1176             // the caller.
1177             // If the job is not yet ready to run, there is nothing more to do -- we are
1178             // now just waiting for one of its controllers to change state and schedule
1179             // the job appropriately.
1180             if (isReadyToBeExecutedLocked(jobStatus)) {
1181                 // This is a new job, we can just immediately put it on the pending
1182                 // list and try to run it.
1183                 mJobPackageTracker.notePending(jobStatus);
1184                 addOrderedItem(mPendingJobs, jobStatus, mPendingJobComparator);
1185                 maybeRunPendingJobsLocked();
1186             } else {
1187                 evaluateControllerStatesLocked(jobStatus);
1188             }
1189         }
1190         return JobScheduler.RESULT_SUCCESS;
1191     }
1192 
1193     public List<JobInfo> getPendingJobs(int uid) {
1194         synchronized (mLock) {
1195             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1196             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
1197             for (int i = jobs.size() - 1; i >= 0; i--) {
1198                 JobStatus job = jobs.get(i);
1199                 outList.add(job.getJob());
1200             }
1201             return outList;
1202         }
1203     }
1204 
1205     public JobInfo getPendingJob(int uid, int jobId) {
1206         synchronized (mLock) {
1207             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1208             for (int i = jobs.size() - 1; i >= 0; i--) {
1209                 JobStatus job = jobs.get(i);
1210                 if (job.getJobId() == jobId) {
1211                     return job.getJob();
1212                 }
1213             }
1214             return null;
1215         }
1216     }
1217 
1218     private void cancelJobsForUserLocked(int userHandle) {
1219         final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
1220         for (int i = 0; i < jobsForUser.size(); i++) {
1221             JobStatus toRemove = jobsForUser.get(i);
1222             // There's no guarantee that the process has been stopped by the time we get here,
1223             // but since this is a user-initiated action, it should be fine to just put USER
1224             // instead of UNINSTALL or DISABLED.
1225             cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
1226                     JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
1227         }
1228     }
1229 
1230     private void cancelJobsForNonExistentUsers() {
1231         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
1232         synchronized (mLock) {
1233             mJobs.removeJobsOfUnlistedUsers(umi.getUserIds());
1234         }
1235     }
1236 
1237     private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
1238             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
1239         if ("android".equals(pkgName)) {
1240             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
1241             return;
1242         }
1243         final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1244         for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1245             final JobStatus job = jobsForUid.get(i);
1246             if (job.getSourcePackageName().equals(pkgName)) {
1247                 cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
1248             }
1249         }
1250     }
1251 
1252     /**
1253      * Entry point from client to cancel all jobs originating from their uid.
1254      * This will remove the job from the master list, and cancel the job if it was staged for
1255      * execution or being executed.
1256      *
1257      * @param uid Uid to check against for removal of a job.
1258      */
1259     public boolean cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
1260             int internalReasonCode, String debugReason) {
1261         if (uid == Process.SYSTEM_UID) {
1262             Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
1263             return false;
1264         }
1265 
1266         boolean jobsCanceled = false;
1267         synchronized (mLock) {
1268             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1269             for (int i = 0; i < jobsForUid.size(); i++) {
1270                 JobStatus toRemove = jobsForUid.get(i);
1271                 cancelJobImplLocked(toRemove, null, reason, internalReasonCode, debugReason);
1272                 jobsCanceled = true;
1273             }
1274         }
1275         return jobsCanceled;
1276     }
1277 
1278     /**
1279      * Entry point from client to cancel the job corresponding to the jobId provided.
1280      * This will remove the job from the master list, and cancel the job if it was staged for
1281      * execution or being executed.
1282      *
1283      * @param uid   Uid of the calling client.
1284      * @param jobId Id of the job, provided at schedule-time.
1285      */
1286     private boolean cancelJob(int uid, int jobId, int callingUid,
1287             @JobParameters.StopReason int reason) {
1288         JobStatus toCancel;
1289         synchronized (mLock) {
1290             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
1291             if (toCancel != null) {
1292                 cancelJobImplLocked(toCancel, null, reason,
1293                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
1294                         "cancel() called by app, callingUid=" + callingUid
1295                                 + " uid=" + uid + " jobId=" + jobId);
1296             }
1297             return (toCancel != null);
1298         }
1299     }
1300 
1301     /**
1302      * Cancel the given job, stopping it if it's currently executing.  If {@code incomingJob}
1303      * is null, the cancelled job is removed outright from the system.  If
1304      * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
1305      * currently scheduled jobs.
1306      */
1307     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
1308             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
1309         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1310         cancelled.unprepareLocked();
1311         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1312         // Remove from pending queue.
1313         if (mPendingJobs.remove(cancelled)) {
1314             mJobPackageTracker.noteNonpending(cancelled);
1315         }
1316         // Cancel if running.
1317         stopJobOnServiceContextLocked(cancelled, reason, internalReasonCode, debugReason);
1318         // If this is a replacement, bring in the new version of the job
1319         if (incomingJob != null) {
1320             if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
1321             startTrackingJobLocked(incomingJob, cancelled);
1322         }
1323         reportActiveLocked();
1324     }
1325 
1326     void updateUidState(int uid, int procState) {
1327         synchronized (mLock) {
1328             final int prevPriority = mUidPriorityOverride.get(uid, JobInfo.PRIORITY_DEFAULT);
1329             if (procState == ActivityManager.PROCESS_STATE_TOP) {
1330                 // Only use this if we are exactly the top app.  All others can live
1331                 // with just the foreground priority.  This means that persistent processes
1332                 // can never be the top app priority...  that is fine.
1333                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
1334             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
1335                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
1336             } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
1337                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
1338             } else {
1339                 mUidPriorityOverride.delete(uid);
1340             }
1341             final int newPriority = mUidPriorityOverride.get(uid, JobInfo.PRIORITY_DEFAULT);
1342             if (prevPriority != newPriority) {
1343                 if (DEBUG) {
1344                     Slog.d(TAG, "UID " + uid + " priority changed from " + prevPriority
1345                             + " to " + newPriority);
1346                 }
1347                 for (int c = 0; c < mControllers.size(); ++c) {
1348                     mControllers.get(c).onUidPriorityChangedLocked(uid, newPriority);
1349                 }
1350             }
1351         }
1352     }
1353 
1354     @Override
1355     public void onDeviceIdleStateChanged(boolean deviceIdle) {
1356         synchronized (mLock) {
1357             if (DEBUG) {
1358                 Slog.d(TAG, "Doze state changed: " + deviceIdle);
1359             }
1360             if (deviceIdle) {
1361                 // When becoming idle, make sure no jobs are actively running,
1362                 // except those using the idle exemption flag.
1363                 for (int i=0; i<mActiveServices.size(); i++) {
1364                     JobServiceContext jsc = mActiveServices.get(i);
1365                     final JobStatus executing = jsc.getRunningJobLocked();
1366                     if (executing != null && !executing.canRunInDoze()) {
1367                         jsc.cancelExecutingJobLocked(JobParameters.STOP_REASON_DEVICE_STATE,
1368                                 JobParameters.INTERNAL_STOP_REASON_DEVICE_IDLE,
1369                                 "cancelled due to doze");
1370                     }
1371                 }
1372             } else {
1373                 // When coming out of idle, allow thing to start back up.
1374                 if (mReadyToRock) {
1375                     if (mLocalDeviceIdleController != null) {
1376                         if (!mReportedActive) {
1377                             mReportedActive = true;
1378                             mLocalDeviceIdleController.setJobsActive(true);
1379                         }
1380                     }
1381                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1382                 }
1383             }
1384         }
1385     }
1386 
1387     @Override
1388     public void onRestrictedBucketChanged(List<JobStatus> jobs) {
1389         final int len = jobs.size();
1390         if (len == 0) {
1391             Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
1392             return;
1393         }
1394         synchronized (mLock) {
1395             for (int i = 0; i < len; ++i) {
1396                 JobStatus js = jobs.get(i);
1397                 for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
1398                     // Effective standby bucket can change after this in some situations so use
1399                     // the real bucket so that the job is tracked by the controllers.
1400                     if (js.getStandbyBucket() == RESTRICTED_INDEX) {
1401                         mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
1402                     } else {
1403                         mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
1404                     }
1405                 }
1406             }
1407         }
1408         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1409     }
1410 
1411     void reportActiveLocked() {
1412         // active is true if pending queue contains jobs OR some job is running.
1413         boolean active = mPendingJobs.size() > 0;
1414         if (mPendingJobs.size() <= 0) {
1415             for (int i=0; i<mActiveServices.size(); i++) {
1416                 final JobServiceContext jsc = mActiveServices.get(i);
1417                 final JobStatus job = jsc.getRunningJobLocked();
1418                 if (job != null
1419                         && !job.canRunInDoze()
1420                         && !job.dozeWhitelisted
1421                         && !job.uidActive) {
1422                     // We will report active if we have a job running and it is not an exception
1423                     // due to being in the foreground or whitelisted.
1424                     active = true;
1425                     break;
1426                 }
1427             }
1428         }
1429 
1430         if (mReportedActive != active) {
1431             mReportedActive = active;
1432             if (mLocalDeviceIdleController != null) {
1433                 mLocalDeviceIdleController.setJobsActive(active);
1434             }
1435         }
1436     }
1437 
1438     void reportAppUsage(String packageName, int userId) {
1439         // This app just transitioned into interactive use or near equivalent, so we should
1440         // take a look at its job state for feedback purposes.
1441     }
1442 
1443     /**
1444      * Initializes the system service.
1445      * <p>
1446      * Subclasses must define a single argument constructor that accepts the context
1447      * and passes it to super.
1448      * </p>
1449      *
1450      * @param context The system server context.
1451      */
1452     public JobSchedulerService(Context context) {
1453         super(context);
1454 
1455         mLocalPM = LocalServices.getService(PackageManagerInternal.class);
1456         mActivityManagerInternal = Objects.requireNonNull(
1457                 LocalServices.getService(ActivityManagerInternal.class));
1458 
1459         mHandler = new JobHandler(context.getMainLooper());
1460         mConstants = new Constants();
1461         mConstantsObserver = new ConstantsObserver();
1462         mJobSchedulerStub = new JobSchedulerStub();
1463 
1464         mConcurrencyManager = new JobConcurrencyManager(this);
1465 
1466         // Set up the app standby bucketing tracker
1467         mStandbyTracker = new StandbyTracker();
1468         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1469         mQuotaTracker = new CountQuotaTracker(context, QUOTA_CATEGORIZER);
1470         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
1471                 mConstants.API_QUOTA_SCHEDULE_COUNT,
1472                 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
1473         // Log at most once per minute.
1474         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000);
1475 
1476         mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
1477         mAppStandbyInternal.addListener(mStandbyTracker);
1478 
1479         // The job store needs to call back
1480         publishLocalService(JobSchedulerInternal.class, new LocalService());
1481 
1482         // Initialize the job store and set up any persisted jobs
1483         mJobs = JobStore.initAndGet(this);
1484 
1485         // Create the controllers.
1486         mControllers = new ArrayList<StateController>();
1487         final ConnectivityController connectivityController = new ConnectivityController(this);
1488         mControllers.add(connectivityController);
1489         mControllers.add(new TimeController(this));
1490         final IdleController idleController = new IdleController(this);
1491         mControllers.add(idleController);
1492         mBatteryController = new BatteryController(this);
1493         mControllers.add(mBatteryController);
1494         mStorageController = new StorageController(this);
1495         mControllers.add(mStorageController);
1496         final BackgroundJobsController backgroundJobsController =
1497                 new BackgroundJobsController(this);
1498         mControllers.add(backgroundJobsController);
1499         mControllers.add(new ContentObserverController(this));
1500         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
1501         mControllers.add(mDeviceIdleJobsController);
1502         mQuotaController =
1503                 new QuotaController(this, backgroundJobsController, connectivityController);
1504         mControllers.add(mQuotaController);
1505         mControllers.add(new ComponentController(this));
1506 
1507         mRestrictiveControllers = new ArrayList<>();
1508         mRestrictiveControllers.add(mBatteryController);
1509         mRestrictiveControllers.add(connectivityController);
1510         mRestrictiveControllers.add(idleController);
1511 
1512         // Create restrictions
1513         mJobRestrictions = new ArrayList<>();
1514         mJobRestrictions.add(new ThermalStatusRestriction(this));
1515 
1516         mSystemGalleryPackage = Objects.requireNonNull(
1517                 context.getString(R.string.config_systemGallery));
1518 
1519         // If the job store determined that it can't yet reschedule persisted jobs,
1520         // we need to start watching the clock.
1521         if (!mJobs.jobTimesInflatedValid()) {
1522             Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1523             context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1524         }
1525     }
1526 
1527     private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1528         @Override
1529         public void onReceive(Context context, Intent intent) {
1530             if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1531                 // When we reach clock sanity, recalculate the temporal windows
1532                 // of all affected jobs.
1533                 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
1534                     Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1535 
1536                     // We've done our job now, so stop watching the time.
1537                     context.unregisterReceiver(this);
1538 
1539                     // And kick off the work to update the affected jobs, using a secondary
1540                     // thread instead of chugging away here on the main looper thread.
1541                     new Thread(mJobTimeUpdater, "JobSchedulerTimeSetReceiver").start();
1542                 }
1543             }
1544         }
1545     };
1546 
1547     private final Runnable mJobTimeUpdater = () -> {
1548         Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
1549 
1550         final ArrayList<JobStatus> toRemove = new ArrayList<>();
1551         final ArrayList<JobStatus> toAdd = new ArrayList<>();
1552         synchronized (mLock) {
1553             // Note: we intentionally both look up the existing affected jobs and replace them
1554             // with recalculated ones inside the same lock lifetime.
1555             getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1556 
1557             // Now, at each position [i], we have both the existing JobStatus
1558             // and the one that replaces it.
1559             final int N = toAdd.size();
1560             for (int i = 0; i < N; i++) {
1561                 final JobStatus oldJob = toRemove.get(i);
1562                 final JobStatus newJob = toAdd.get(i);
1563                 if (DEBUG) {
1564                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
1565                 }
1566                 cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
1567                         JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED, "deferred rtc calculation");
1568             }
1569         }
1570     };
1571 
1572     @Override
1573     public void onStart() {
1574         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1575     }
1576 
1577     @Override
1578     public void onBootPhase(int phase) {
1579         if (PHASE_SYSTEM_SERVICES_READY == phase) {
1580             mConstantsObserver.start();
1581             for (StateController controller : mControllers) {
1582                 controller.onSystemServicesReady();
1583             }
1584 
1585             mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
1586                     LocalServices.getService(AppStateTracker.class));
1587 
1588             // Register br for package removals and user removals.
1589             final IntentFilter filter = new IntentFilter();
1590             filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
1591             filter.addAction(Intent.ACTION_PACKAGE_ADDED);
1592             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1593             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1594             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1595             filter.addDataScheme("package");
1596             getContext().registerReceiverAsUser(
1597                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1598             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1599             userFilter.addAction(Intent.ACTION_USER_ADDED);
1600             getContext().registerReceiverAsUser(
1601                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
1602             try {
1603                 ActivityManager.getService().registerUidObserver(mUidObserver,
1604                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
1605                         | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1606                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
1607             } catch (RemoteException e) {
1608                 // ignored; both services live in system_server
1609             }
1610 
1611             mConcurrencyManager.onSystemReady();
1612 
1613             // Remove any jobs that are not associated with any of the current users.
1614             cancelJobsForNonExistentUsers();
1615 
1616             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
1617                 mJobRestrictions.get(i).onSystemServicesReady();
1618             }
1619         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
1620             synchronized (mLock) {
1621                 // Let's go!
1622                 mReadyToRock = true;
1623                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1624                         BatteryStats.SERVICE_NAME));
1625                 mLocalDeviceIdleController =
1626                         LocalServices.getService(DeviceIdleInternal.class);
1627                 // Create the "runners".
1628                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1629                     mActiveServices.add(
1630                             new JobServiceContext(this, mConcurrencyManager, mBatteryStats,
1631                                     mJobPackageTracker, getContext().getMainLooper()));
1632                 }
1633                 // Attach jobs to their controllers.
1634                 mJobs.forEachJob((job) -> {
1635                     for (int controller = 0; controller < mControllers.size(); controller++) {
1636                         final StateController sc = mControllers.get(controller);
1637                         sc.maybeStartTrackingJobLocked(job, null);
1638                     }
1639                 });
1640                 // GO GO GO!
1641                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1642             }
1643         }
1644     }
1645 
1646     /**
1647      * Called when we have a job status object that we need to insert in our
1648      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1649      * about.
1650      */
1651     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1652         if (!jobStatus.isPreparedLocked()) {
1653             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1654         }
1655         jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
1656         final boolean update = mJobs.add(jobStatus);
1657         if (mReadyToRock) {
1658             for (int i = 0; i < mControllers.size(); i++) {
1659                 StateController controller = mControllers.get(i);
1660                 if (update) {
1661                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1662                 }
1663                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1664             }
1665         }
1666     }
1667 
1668     /**
1669      * Called when we want to remove a JobStatus object that we've finished executing.
1670      * @return true if the job was removed.
1671      */
1672     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1673             boolean removeFromPersisted) {
1674         // Deal with any remaining work items in the old job.
1675         jobStatus.stopTrackingJobLocked(incomingJob);
1676 
1677         // Remove from store as well as controllers.
1678         final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
1679         if (removed && mReadyToRock) {
1680             for (int i=0; i<mControllers.size(); i++) {
1681                 StateController controller = mControllers.get(i);
1682                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1683             }
1684         }
1685         return removed;
1686     }
1687 
1688     private boolean stopJobOnServiceContextLocked(JobStatus job,
1689             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
1690         for (int i = 0; i < mActiveServices.size(); i++) {
1691             JobServiceContext jsc = mActiveServices.get(i);
1692             final JobStatus executing = jsc.getRunningJobLocked();
1693             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1694                 jsc.cancelExecutingJobLocked(reason, internalReasonCode, debugReason);
1695                 return true;
1696             }
1697         }
1698         return false;
1699     }
1700 
1701     void noteJobsPending(List<JobStatus> jobs) {
1702         for (int i = jobs.size() - 1; i >= 0; i--) {
1703             JobStatus job = jobs.get(i);
1704             mJobPackageTracker.notePending(job);
1705         }
1706     }
1707 
1708     void noteJobsNonpending(List<JobStatus> jobs) {
1709         for (int i = jobs.size() - 1; i >= 0; i--) {
1710             JobStatus job = jobs.get(i);
1711             mJobPackageTracker.noteNonpending(job);
1712         }
1713     }
1714 
1715     /**
1716      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1717      * specify an override deadline on a failed job (the failed job will run even though it's not
1718      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1719      * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
1720      *
1721      * @param failureToReschedule Provided job status that we will reschedule.
1722      * @return A newly instantiated JobStatus with the same constraints as the last job except
1723      * with adjusted timing constraints.
1724      *
1725      * @see #maybeQueueReadyJobsForExecutionLocked
1726      */
1727     @VisibleForTesting
1728     JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1729         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
1730         final JobInfo job = failureToReschedule.getJob();
1731 
1732         final long initialBackoffMillis = job.getInitialBackoffMillis();
1733         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1734         long delayMillis;
1735 
1736         switch (job.getBackoffPolicy()) {
1737             case JobInfo.BACKOFF_POLICY_LINEAR: {
1738                 long backoff = initialBackoffMillis;
1739                 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
1740                     backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
1741                 }
1742                 delayMillis = backoff * backoffAttempts;
1743             } break;
1744             default:
1745                 if (DEBUG) {
1746                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1747                 }
1748             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1749                 long backoff = initialBackoffMillis;
1750                 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
1751                     backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
1752                 }
1753                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1754             } break;
1755         }
1756         delayMillis =
1757                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1758         JobStatus newJob = new JobStatus(failureToReschedule,
1759                 elapsedNowMillis + delayMillis,
1760                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
1761                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
1762         if (job.isPeriodic()) {
1763             newJob.setOriginalLatestRunTimeElapsed(
1764                     failureToReschedule.getOriginalLatestRunTimeElapsed());
1765         }
1766         for (int ic=0; ic<mControllers.size(); ic++) {
1767             StateController controller = mControllers.get(ic);
1768             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1769         }
1770         return newJob;
1771     }
1772 
1773     /**
1774      * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
1775      * does not cause a job's period to be larger than requested (eg: if the requested period is
1776      * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
1777      * and try to optimize scheduling if the current job finished less than this amount of time to
1778      * the start of the next period
1779      */
1780     private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
1781 
1782     /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
1783     public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
1784 
1785     /**
1786      * Called after a periodic has executed so we can reschedule it. We take the last execution
1787      * time of the job to be the time of completion (i.e. the time at which this function is
1788      * called).
1789      * <p>This could be inaccurate b/c the job can run for as long as
1790      * {@link Constants#DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS}, but
1791      * will lead to underscheduling at least, rather than if we had taken the last execution time
1792      * to be the start of the execution.
1793      *
1794      * @return A new job representing the execution criteria for this instantiation of the
1795      * recurring job.
1796      */
1797     @VisibleForTesting
1798     JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1799         final long elapsedNow = sElapsedRealtimeClock.millis();
1800         final long newLatestRuntimeElapsed;
1801         // Make sure period is in the interval [min_possible_period, max_possible_period].
1802         final long period = Math.max(JobInfo.getMinPeriodMillis(),
1803                 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
1804         // Make sure flex is in the interval [min_possible_flex, period].
1805         final long flex = Math.max(JobInfo.getMinFlexMillis(),
1806                 Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
1807         long rescheduleBuffer = 0;
1808 
1809         long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
1810         if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
1811             Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
1812             olrte = elapsedNow;
1813         }
1814         final long latestRunTimeElapsed = olrte;
1815 
1816         final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
1817         if (elapsedNow > latestRunTimeElapsed) {
1818             // The job ran past its expected run window. Have it count towards the current window
1819             // and schedule a new job for the next window.
1820             if (DEBUG) {
1821                 Slog.i(TAG, "Periodic job ran after its intended window.");
1822             }
1823             long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
1824             if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
1825                     (period - flex) / 2)) {
1826                 if (DEBUG) {
1827                     Slog.d(TAG, "Custom flex job ran too close to next window.");
1828                 }
1829                 // For custom flex periods, if the job was run too close to the next window,
1830                 // skip the next window and schedule for the following one.
1831                 numSkippedWindows += 1;
1832             }
1833             newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
1834         } else {
1835             newLatestRuntimeElapsed = latestRunTimeElapsed + period;
1836             if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
1837                 // Add a little buffer to the start of the next window so the job doesn't run
1838                 // too soon after this completed one.
1839                 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
1840             }
1841         }
1842 
1843         if (newLatestRuntimeElapsed < elapsedNow) {
1844             Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
1845                     + newLatestRuntimeElapsed);
1846             return new JobStatus(periodicToReschedule,
1847                     elapsedNow + period - flex, elapsedNow + period,
1848                     0 /* backoffAttempt */,
1849                     sSystemClock.millis() /* lastSuccessfulRunTime */,
1850                     periodicToReschedule.getLastFailedRunTime());
1851         }
1852 
1853         final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
1854                 - Math.min(flex, period - rescheduleBuffer);
1855 
1856         if (DEBUG) {
1857             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1858                     newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
1859                     + "]s");
1860         }
1861         return new JobStatus(periodicToReschedule,
1862                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1863                 0 /* backoffAttempt */,
1864                 sSystemClock.millis() /* lastSuccessfulRunTime */,
1865                 periodicToReschedule.getLastFailedRunTime());
1866     }
1867 
1868     // JobCompletedListener implementations.
1869 
1870     /**
1871      * A job just finished executing. We fetch the
1872      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1873      * whether we want to reschedule we re-add it to the controllers.
1874      *
1875      * @param jobStatus       Completed job.
1876      * @param needsReschedule Whether the implementing class should reschedule this job.
1877      */
1878     @Override
1879     public void onJobCompletedLocked(JobStatus jobStatus, int debugStopReason,
1880             boolean needsReschedule) {
1881         if (DEBUG) {
1882             Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
1883                     + ", reschedule=" + needsReschedule);
1884         }
1885 
1886         mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
1887         mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
1888         mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
1889 
1890         if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL
1891                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) {
1892             // The job should have already been cleared from the rest of the JS tracking. No need
1893             // to go through all that flow again.
1894             jobStatus.unprepareLocked();
1895             reportActiveLocked();
1896             return;
1897         }
1898 
1899         // Intentionally not checking expedited job quota here. An app can't find out if it's run
1900         // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
1901         // EJ will just be demoted to a regular job if the app has no EJ quota left.
1902 
1903         // If the job wants to be rescheduled, we first need to make the next upcoming
1904         // job so we can transfer any appropriate state over from the previous job when
1905         // we stop it.
1906         final JobStatus rescheduledJob = needsReschedule
1907                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1908         if (rescheduledJob != null
1909                 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
1910                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
1911             rescheduledJob.disallowRunInBatterySaverAndDoze();
1912         }
1913 
1914         // Do not write back immediately if this is a periodic job. The job may get lost if system
1915         // shuts down before it is added back.
1916         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1917             if (DEBUG) {
1918                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1919             }
1920             JobStatus newJs = mJobs.getJobByUidAndJobId(jobStatus.getUid(), jobStatus.getJobId());
1921             if (newJs != null) {
1922                 // This job was stopped because the app scheduled a new job with the same job ID.
1923                 // Check if the new job is ready to run.
1924                 mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget();
1925             }
1926             return;
1927         }
1928 
1929         if (rescheduledJob != null) {
1930             try {
1931                 rescheduledJob.prepareLocked();
1932             } catch (SecurityException e) {
1933                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1934             }
1935             startTrackingJobLocked(rescheduledJob, jobStatus);
1936         } else if (jobStatus.getJob().isPeriodic()) {
1937             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1938             try {
1939                 rescheduledPeriodic.prepareLocked();
1940             } catch (SecurityException e) {
1941                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1942             }
1943             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1944         }
1945         jobStatus.unprepareLocked();
1946         reportActiveLocked();
1947     }
1948 
1949     // StateChangedListener implementations.
1950 
1951     /**
1952      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1953      * some controller's state has changed, so as to run through the list of jobs and start/stop
1954      * any that are eligible.
1955      */
1956     @Override
1957     public void onControllerStateChanged() {
1958         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1959     }
1960 
1961     @Override
1962     public void onRunJobNow(JobStatus jobStatus) {
1963         if (jobStatus == null) {
1964             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1965         } else {
1966             mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget();
1967         }
1968     }
1969 
1970     final private class JobHandler extends Handler {
1971 
1972         public JobHandler(Looper looper) {
1973             super(looper);
1974         }
1975 
1976         @Override
1977         public void handleMessage(Message message) {
1978             synchronized (mLock) {
1979                 if (!mReadyToRock) {
1980                     return;
1981                 }
1982                 switch (message.what) {
1983                     case MSG_CHECK_INDIVIDUAL_JOB: {
1984                         JobStatus js = (JobStatus) message.obj;
1985                         if (js != null) {
1986                             if (isReadyToBeExecutedLocked(js)) {
1987                                 mJobPackageTracker.notePending(js);
1988                                 addOrderedItem(mPendingJobs, js, mPendingJobComparator);
1989                             }
1990                         } else {
1991                             Slog.e(TAG, "Given null job to check individually");
1992                         }
1993                     } break;
1994                     case MSG_CHECK_JOB:
1995                         if (DEBUG) {
1996                             Slog.d(TAG, "MSG_CHECK_JOB");
1997                         }
1998                         removeMessages(MSG_CHECK_JOB);
1999                         if (mReportedActive) {
2000                             // if jobs are currently being run, queue all ready jobs for execution.
2001                             queueReadyJobsForExecutionLocked();
2002                         } else {
2003                             // Check the list of jobs and run some of them if we feel inclined.
2004                             maybeQueueReadyJobsForExecutionLocked();
2005                         }
2006                         break;
2007                     case MSG_CHECK_JOB_GREEDY:
2008                         if (DEBUG) {
2009                             Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
2010                         }
2011                         queueReadyJobsForExecutionLocked();
2012                         break;
2013                     case MSG_STOP_JOB:
2014                         cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
2015                                 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
2016                                 "app no longer allowed to run");
2017                         break;
2018 
2019                     case MSG_UID_STATE_CHANGED: {
2020                         final int uid = message.arg1;
2021                         final int procState = message.arg2;
2022                         updateUidState(uid, procState);
2023                         break;
2024                     }
2025                     case MSG_UID_GONE: {
2026                         final int uid = message.arg1;
2027                         final boolean disabled = message.arg2 != 0;
2028                         updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
2029                         if (disabled) {
2030                             cancelJobsForUid(uid,
2031                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
2032                                     JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
2033                                     "uid gone");
2034                         }
2035                         synchronized (mLock) {
2036                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2037                         }
2038                         break;
2039                     }
2040                     case MSG_UID_ACTIVE: {
2041                         final int uid = message.arg1;
2042                         synchronized (mLock) {
2043                             mDeviceIdleJobsController.setUidActiveLocked(uid, true);
2044                         }
2045                         break;
2046                     }
2047                     case MSG_UID_IDLE: {
2048                         final int uid = message.arg1;
2049                         final boolean disabled = message.arg2 != 0;
2050                         if (disabled) {
2051                             cancelJobsForUid(uid,
2052                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
2053                                     JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
2054                                     "app uid idle");
2055                         }
2056                         synchronized (mLock) {
2057                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2058                         }
2059                         break;
2060                     }
2061 
2062                 }
2063                 maybeRunPendingJobsLocked();
2064             }
2065         }
2066     }
2067 
2068     /**
2069      * Check if a job is restricted by any of the declared {@link JobRestriction}s.
2070      * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not
2071      * be restricted, thus we won't even perform the check, but simply return null early.
2072      *
2073      * @param job to be checked
2074      * @return the first {@link JobRestriction} restricting the given job that has been found; null
2075      * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP}
2076      * or higher.
2077      */
2078     private JobRestriction checkIfRestricted(JobStatus job) {
2079         if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) {
2080             // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted
2081             return null;
2082         }
2083         for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
2084             final JobRestriction restriction = mJobRestrictions.get(i);
2085             if (restriction.isJobRestricted(job)) {
2086                 return restriction;
2087             }
2088         }
2089         return null;
2090     }
2091 
2092     private void stopNonReadyActiveJobsLocked() {
2093         for (int i=0; i<mActiveServices.size(); i++) {
2094             JobServiceContext serviceContext = mActiveServices.get(i);
2095             final JobStatus running = serviceContext.getRunningJobLocked();
2096             if (running == null) {
2097                 continue;
2098             }
2099             if (!running.isReady()) {
2100                 // If a restricted job doesn't have dynamic constraints satisfied, assume that's
2101                 // the reason the job is being stopped, instead of because of other constraints
2102                 // not being satisfied.
2103                 if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
2104                         && !running.areDynamicConstraintsSatisfied()) {
2105                     serviceContext.cancelExecutingJobLocked(
2106                             running.getStopReason(),
2107                             JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET,
2108                             "cancelled due to restricted bucket");
2109                 } else {
2110                     serviceContext.cancelExecutingJobLocked(
2111                             running.getStopReason(),
2112                             JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
2113                             "cancelled due to unsatisfied constraints");
2114                 }
2115             } else {
2116                 final JobRestriction restriction = checkIfRestricted(running);
2117                 if (restriction != null) {
2118                     final int internalReasonCode = restriction.getInternalReason();
2119                     serviceContext.cancelExecutingJobLocked(restriction.getReason(),
2120                             internalReasonCode,
2121                             "restricted due to "
2122                                     + JobParameters.getInternalReasonCodeDescription(
2123                                     internalReasonCode));
2124                 }
2125             }
2126         }
2127     }
2128 
2129     /**
2130      * Run through list of jobs and execute all possible - at least one is expired so we do
2131      * as many as we can.
2132      */
2133     @GuardedBy("mLock")
2134     private void queueReadyJobsForExecutionLocked() {
2135         // This method will check and capture all ready jobs, so we don't need to keep any messages
2136         // in the queue.
2137         mHandler.removeMessages(MSG_CHECK_JOB_GREEDY);
2138         mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB);
2139         // MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready
2140         // jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue.
2141         mHandler.removeMessages(MSG_CHECK_JOB);
2142         if (DEBUG) {
2143             Slog.d(TAG, "queuing all ready jobs for execution:");
2144         }
2145         noteJobsNonpending(mPendingJobs);
2146         mPendingJobs.clear();
2147         stopNonReadyActiveJobsLocked();
2148         mJobs.forEachJob(mReadyQueueFunctor);
2149         mReadyQueueFunctor.postProcessLocked();
2150 
2151         if (DEBUG) {
2152             final int queuedJobs = mPendingJobs.size();
2153             if (queuedJobs == 0) {
2154                 Slog.d(TAG, "No jobs pending.");
2155             } else {
2156                 Slog.d(TAG, queuedJobs + " jobs queued.");
2157             }
2158         }
2159     }
2160 
2161     final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
2162         final ArrayList<JobStatus> newReadyJobs = new ArrayList<>();
2163 
2164         @Override
2165         public void accept(JobStatus job) {
2166             if (isReadyToBeExecutedLocked(job)) {
2167                 if (DEBUG) {
2168                     Slog.d(TAG, "    queued " + job.toShortString());
2169                 }
2170                 newReadyJobs.add(job);
2171             } else {
2172                 evaluateControllerStatesLocked(job);
2173             }
2174         }
2175 
2176         @GuardedBy("mLock")
2177         private void postProcessLocked() {
2178             noteJobsPending(newReadyJobs);
2179             mPendingJobs.addAll(newReadyJobs);
2180             if (mPendingJobs.size() > 1) {
2181                 mPendingJobComparator.refreshLocked();
2182                 mPendingJobs.sort(mPendingJobComparator);
2183             }
2184 
2185             newReadyJobs.clear();
2186         }
2187     }
2188 
2189     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
2190 
2191     /**
2192      * The state of at least one job has changed. Here is where we could enforce various
2193      * policies on when we want to execute jobs.
2194      */
2195     final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
2196         int forceBatchedCount;
2197         int unbatchedCount;
2198         final List<JobStatus> runnableJobs = new ArrayList<>();
2199 
2200         public MaybeReadyJobQueueFunctor() {
2201             reset();
2202         }
2203 
2204         // Functor method invoked for each job via JobStore.forEachJob()
2205         @Override
2206         public void accept(JobStatus job) {
2207             if (isReadyToBeExecutedLocked(job)) {
2208                 if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(),
2209                         job.getJob().getService().getPackageName())) {
2210                     Slog.w(TAG, "Aborting job " + job.getUid() + ":"
2211                             + job.getJob().toString() + " -- package not allowed to start");
2212                     mHandler.obtainMessage(MSG_STOP_JOB,
2213                             JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
2214                             .sendToTarget();
2215                     return;
2216                 }
2217 
2218                 final boolean shouldForceBatchJob;
2219                 if (job.shouldTreatAsExpeditedJob()) {
2220                     // Never batch expedited jobs, even for RESTRICTED apps.
2221                     shouldForceBatchJob = false;
2222                 } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
2223                     // Restricted jobs must always be batched
2224                     shouldForceBatchJob = true;
2225                 } else if (job.getNumFailures() > 0) {
2226                     shouldForceBatchJob = false;
2227                 } else {
2228                     final long nowElapsed = sElapsedRealtimeClock.millis();
2229                     final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0
2230                             && nowElapsed - job.getFirstForceBatchedTimeElapsed()
2231                             >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
2232                     shouldForceBatchJob =
2233                             mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
2234                                     && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
2235                                     && !batchDelayExpired;
2236                 }
2237 
2238                 if (shouldForceBatchJob) {
2239                     // Force batching non-ACTIVE jobs. Don't include them in the other counts.
2240                     forceBatchedCount++;
2241                     if (job.getFirstForceBatchedTimeElapsed() == 0) {
2242                         job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis());
2243                     }
2244                 } else {
2245                     unbatchedCount++;
2246                 }
2247                 runnableJobs.add(job);
2248             } else {
2249                 evaluateControllerStatesLocked(job);
2250             }
2251         }
2252 
2253         @GuardedBy("mLock")
2254         @VisibleForTesting
2255         void postProcessLocked() {
2256             if (unbatchedCount > 0
2257                     || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) {
2258                 if (DEBUG) {
2259                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
2260                 }
2261                 noteJobsPending(runnableJobs);
2262                 mPendingJobs.addAll(runnableJobs);
2263                 if (mPendingJobs.size() > 1) {
2264                     mPendingJobComparator.refreshLocked();
2265                     mPendingJobs.sort(mPendingJobComparator);
2266                 }
2267             } else {
2268                 if (DEBUG) {
2269                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
2270                 }
2271             }
2272 
2273             // Be ready for next time
2274             reset();
2275         }
2276 
2277         @VisibleForTesting
2278         void reset() {
2279             forceBatchedCount = 0;
2280             unbatchedCount = 0;
2281             runnableJobs.clear();
2282         }
2283     }
2284     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
2285 
2286     @GuardedBy("mLock")
2287     private void maybeQueueReadyJobsForExecutionLocked() {
2288         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
2289 
2290         noteJobsNonpending(mPendingJobs);
2291         mPendingJobs.clear();
2292         stopNonReadyActiveJobsLocked();
2293         mJobs.forEachJob(mMaybeQueueFunctor);
2294         mMaybeQueueFunctor.postProcessLocked();
2295     }
2296 
2297     /** Returns true if both the calling and source users for the job are started. */
2298     @GuardedBy("mLock")
2299     public boolean areUsersStartedLocked(final JobStatus job) {
2300         boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
2301         if (job.getUserId() == job.getSourceUserId()) {
2302             return sourceStarted;
2303         }
2304         return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
2305     }
2306 
2307     /**
2308      * Criteria for moving a job into the pending queue:
2309      *      - It's ready.
2310      *      - It's not pending.
2311      *      - It's not already running on a JSC.
2312      *      - The user that requested the job is running.
2313      *      - The job's standby bucket has come due to be runnable.
2314      *      - The component is enabled and runnable.
2315      */
2316     @VisibleForTesting
2317     @GuardedBy("mLock")
2318     boolean isReadyToBeExecutedLocked(JobStatus job) {
2319         return isReadyToBeExecutedLocked(job, true);
2320     }
2321 
2322     @GuardedBy("mLock")
2323     boolean isReadyToBeExecutedLocked(JobStatus job, boolean rejectActive) {
2324         final boolean jobReady = job.isReady();
2325 
2326         if (DEBUG) {
2327             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2328                     + " ready=" + jobReady);
2329         }
2330 
2331         // This is a condition that is very likely to be false (most jobs that are
2332         // scheduled are sitting there, not ready yet) and very cheap to check (just
2333         // a few conditions on data in JobStatus).
2334         if (!jobReady) {
2335             if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
2336                 Slog.v(TAG, "    NOT READY: " + job);
2337             }
2338             return false;
2339         }
2340 
2341         final boolean jobExists = mJobs.containsJob(job);
2342         final boolean userStarted = areUsersStartedLocked(job);
2343         final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
2344 
2345         if (DEBUG) {
2346             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2347                     + " exists=" + jobExists + " userStarted=" + userStarted
2348                     + " backingUp=" + backingUp);
2349         }
2350 
2351         // These are also fairly cheap to check, though they typically will not
2352         // be conditions we fail.
2353         if (!jobExists || !userStarted || backingUp) {
2354             return false;
2355         }
2356 
2357         if (checkIfRestricted(job) != null) {
2358             return false;
2359         }
2360 
2361         final boolean jobPending = mPendingJobs.contains(job);
2362         final boolean jobActive = rejectActive && mConcurrencyManager.isJobRunningLocked(job);
2363 
2364         if (DEBUG) {
2365             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2366                     + " pending=" + jobPending + " active=" + jobActive);
2367         }
2368 
2369         // These can be a little more expensive (especially jobActive, since we need to
2370         // go through the array of all potentially active jobs), so we are doing them
2371         // later...  but still before checking with the package manager!
2372         if (jobPending || jobActive) {
2373             return false;
2374         }
2375 
2376         // Validate that the defined package+service is still present & viable.
2377         return isComponentUsable(job);
2378     }
2379 
2380     private boolean isComponentUsable(@NonNull JobStatus job) {
2381         final ServiceInfo service = job.serviceInfo;
2382 
2383         if (service == null) {
2384             if (DEBUG) {
2385                 Slog.v(TAG, "isComponentUsable: " + job.toShortString()
2386                         + " component not present");
2387             }
2388             return false;
2389         }
2390 
2391         // Everything else checked out so far, so this is the final yes/no check
2392         final boolean appIsBad = mActivityManagerInternal.isAppBad(
2393                 service.processName, service.applicationInfo.uid);
2394         if (DEBUG && appIsBad) {
2395             Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
2396         }
2397         return !appIsBad;
2398     }
2399 
2400     @VisibleForTesting
2401     void evaluateControllerStatesLocked(final JobStatus job) {
2402         for (int c = mControllers.size() - 1; c >= 0; --c) {
2403             final StateController sc = mControllers.get(c);
2404             sc.evaluateStateLocked(job);
2405         }
2406     }
2407 
2408     /**
2409      * Returns true if non-job constraint components are in place -- if job.isReady() returns true
2410      * and this method returns true, then the job is ready to be executed.
2411      */
2412     public boolean areComponentsInPlaceLocked(JobStatus job) {
2413         // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
2414         // conditions.
2415 
2416         final boolean jobExists = mJobs.containsJob(job);
2417         final boolean userStarted = areUsersStartedLocked(job);
2418         final boolean backingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
2419 
2420         if (DEBUG) {
2421             Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2422                     + " exists=" + jobExists + " userStarted=" + userStarted
2423                     + " backingUp=" + backingUp);
2424         }
2425 
2426         // These are also fairly cheap to check, though they typically will not
2427         // be conditions we fail.
2428         if (!jobExists || !userStarted || backingUp) {
2429             return false;
2430         }
2431 
2432         final JobRestriction restriction = checkIfRestricted(job);
2433         if (restriction != null) {
2434             if (DEBUG) {
2435                 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2436                         + " restricted due to " + restriction.getInternalReason());
2437             }
2438             return false;
2439         }
2440 
2441         // Job pending/active doesn't affect the readiness of a job.
2442 
2443         // The expensive check: validate that the defined package+service is
2444         // still present & viable.
2445         return isComponentUsable(job);
2446     }
2447 
2448     /** Returns the minimum amount of time we should let this job run before timing out. */
2449     public long getMinJobExecutionGuaranteeMs(JobStatus job) {
2450         synchronized (mLock) {
2451             if (job.shouldTreatAsExpeditedJob()) {
2452                 // Don't guarantee RESTRICTED jobs more than 5 minutes.
2453                 return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
2454                         ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
2455                         : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
2456             } else {
2457                 return mConstants.RUNTIME_MIN_GUARANTEE_MS;
2458             }
2459         }
2460     }
2461 
2462     /** Returns the maximum amount of time this job could run for. */
2463     public long getMaxJobExecutionTimeMs(JobStatus job) {
2464         synchronized (mLock) {
2465             return Math.min(mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
2466                     mQuotaController.getMaxJobExecutionTimeMsLocked(job));
2467         }
2468     }
2469 
2470     /**
2471      * Reconcile jobs in the pending queue against available execution contexts.
2472      * A controller can force a job into the pending queue even if it's already running, but
2473      * here is where we decide whether to actually execute it.
2474      */
2475     void maybeRunPendingJobsLocked() {
2476         if (DEBUG) {
2477             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2478         }
2479         mConcurrencyManager.assignJobsToContextsLocked();
2480         reportActiveLocked();
2481     }
2482 
2483     private int adjustJobPriority(int curPriority, JobStatus job) {
2484         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2485             float factor = mJobPackageTracker.getLoadFactor(job);
2486             if (factor >= mConstants.HEAVY_USE_FACTOR) {
2487                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
2488             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
2489                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2490             }
2491         }
2492         return curPriority;
2493     }
2494 
2495     int evaluateJobPriorityLocked(JobStatus job) {
2496         int priority = job.getPriority();
2497         if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
2498             return adjustJobPriority(priority, job);
2499         }
2500         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2501         if (override != 0) {
2502             return adjustJobPriority(override, job);
2503         }
2504         return adjustJobPriority(priority, job);
2505     }
2506 
2507     final class LocalService implements JobSchedulerInternal {
2508 
2509         /**
2510          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2511          * jobs are always considered pending.
2512          */
2513         @Override
2514         public List<JobInfo> getSystemScheduledPendingJobs() {
2515             synchronized (mLock) {
2516                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
2517                 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2518                     if (job.getJob().isPeriodic() || !mConcurrencyManager.isJobRunningLocked(job)) {
2519                         pendingJobs.add(job.getJob());
2520                     }
2521                 });
2522                 return pendingJobs;
2523             }
2524         }
2525 
2526         @Override
2527         public void cancelJobsForUid(int uid, @JobParameters.StopReason int reason,
2528                 int internalReasonCode, String debugReason) {
2529             JobSchedulerService.this.cancelJobsForUid(uid, reason, internalReasonCode, debugReason);
2530         }
2531 
2532         @Override
2533         public void addBackingUpUid(int uid) {
2534             synchronized (mLock) {
2535                 // No need to actually do anything here, since for a full backup the
2536                 // activity manager will kill the process which will kill the job (and
2537                 // cause it to restart, but now it can't run).
2538                 mBackingUpUids.put(uid, uid);
2539             }
2540         }
2541 
2542         @Override
2543         public void removeBackingUpUid(int uid) {
2544             synchronized (mLock) {
2545                 mBackingUpUids.delete(uid);
2546                 // If there are any jobs for this uid, we need to rebuild the pending list
2547                 // in case they are now ready to run.
2548                 if (mJobs.countJobsForUid(uid) > 0) {
2549                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2550                 }
2551             }
2552         }
2553 
2554         @Override
2555         public void clearAllBackingUpUids() {
2556             synchronized (mLock) {
2557                 if (mBackingUpUids.size() > 0) {
2558                     mBackingUpUids.clear();
2559                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2560                 }
2561             }
2562         }
2563 
2564         @Override
2565         public String getMediaBackupPackage() {
2566             return mSystemGalleryPackage;
2567         }
2568 
2569         @Override
2570         public void reportAppUsage(String packageName, int userId) {
2571             JobSchedulerService.this.reportAppUsage(packageName, userId);
2572         }
2573 
2574         @Override
2575         public JobStorePersistStats getPersistStats() {
2576             synchronized (mLock) {
2577                 return new JobStorePersistStats(mJobs.getPersistStats());
2578             }
2579         }
2580     }
2581 
2582     /**
2583      * Tracking of app assignments to standby buckets
2584      */
2585     final class StandbyTracker extends AppIdleStateChangeListener {
2586 
2587         // AppIdleStateChangeListener interface for live updates
2588 
2589         @Override
2590         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
2591                 boolean idle, int bucket, int reason) {
2592             // QuotaController handles this now.
2593         }
2594 
2595         @Override
2596         public void onUserInteractionStarted(String packageName, int userId) {
2597             final int uid = mLocalPM.getPackageUid(packageName,
2598                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2599             if (uid < 0) {
2600                 // Quietly ignore; the case is already logged elsewhere
2601                 return;
2602             }
2603 
2604             long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2605             if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2606                 // Too long ago, not worth logging
2607                 sinceLast = 0L;
2608             }
2609             final DeferredJobCounter counter = new DeferredJobCounter();
2610             synchronized (mLock) {
2611                 mJobs.forEachJobForSourceUid(uid, counter);
2612             }
2613             if (counter.numDeferred() > 0 || sinceLast > 0) {
2614                 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2615                         (BatteryStatsInternal.class);
2616                 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2617                 FrameworkStatsLog.write_non_chained(
2618                         FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
2619                         counter.numDeferred(), sinceLast);
2620             }
2621         }
2622     }
2623 
2624     static class DeferredJobCounter implements Consumer<JobStatus> {
2625         private int mDeferred = 0;
2626 
2627         public int numDeferred() {
2628             return mDeferred;
2629         }
2630 
2631         @Override
2632         public void accept(JobStatus job) {
2633             if (job.getWhenStandbyDeferred() > 0) {
2634                 mDeferred++;
2635             }
2636         }
2637     }
2638 
2639     public static int standbyBucketToBucketIndex(int bucket) {
2640         // Normalize AppStandby constants to indices into our bookkeeping
2641         if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
2642             return NEVER_INDEX;
2643         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
2644             return RESTRICTED_INDEX;
2645         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
2646             return RARE_INDEX;
2647         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
2648             return FREQUENT_INDEX;
2649         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
2650             return WORKING_INDEX;
2651         } else {
2652             return ACTIVE_INDEX;
2653         }
2654     }
2655 
2656     // Static to support external callers
2657     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2658         UsageStatsManagerInternal usageStats = LocalServices.getService(
2659                 UsageStatsManagerInternal.class);
2660         int bucket = usageStats != null
2661                 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2662                 : 0;
2663 
2664         bucket = standbyBucketToBucketIndex(bucket);
2665 
2666         if (DEBUG_STANDBY) {
2667             Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2668         }
2669         return bucket;
2670     }
2671 
2672     /**
2673      * Binder stub trampoline implementation
2674      */
2675     final class JobSchedulerStub extends IJobScheduler.Stub {
2676         /** Cache determination of whether a given app can persist jobs
2677          * key is uid of the calling app; value is undetermined/true/false
2678          */
2679         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2680 
2681         // Enforce that only the app itself (or shared uid participant) can schedule a
2682         // job that runs one of the app's services, as well as verifying that the
2683         // named service properly requires the BIND_JOB_SERVICE permission
2684         private void enforceValidJobRequest(int uid, JobInfo job) {
2685             final PackageManager pm = getContext()
2686                     .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
2687                     .getPackageManager();
2688             final ComponentName service = job.getService();
2689             try {
2690                 ServiceInfo si = pm.getServiceInfo(service,
2691                         PackageManager.MATCH_DIRECT_BOOT_AWARE
2692                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
2693                 if (si == null) {
2694                     throw new IllegalArgumentException("No such service " + service);
2695                 }
2696                 if (si.applicationInfo.uid != uid) {
2697                     throw new IllegalArgumentException("uid " + uid +
2698                             " cannot schedule job in " + service.getPackageName());
2699                 }
2700                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2701                     throw new IllegalArgumentException("Scheduled service " + service
2702                             + " does not require android.permission.BIND_JOB_SERVICE permission");
2703                 }
2704             } catch (NameNotFoundException e) {
2705                 throw new IllegalArgumentException(
2706                         "Tried to schedule job for non-existent component: " + service);
2707             }
2708         }
2709 
2710         private boolean canPersistJobs(int pid, int uid) {
2711             // If we get this far we're good to go; all we need to do now is check
2712             // whether the app is allowed to persist its scheduled work.
2713             final boolean canPersist;
2714             synchronized (mPersistCache) {
2715                 Boolean cached = mPersistCache.get(uid);
2716                 if (cached != null) {
2717                     canPersist = cached.booleanValue();
2718                 } else {
2719                     // Persisting jobs is tantamount to running at boot, so we permit
2720                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2721                     // permission
2722                     int result = getContext().checkPermission(
2723                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2724                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
2725                     mPersistCache.put(uid, canPersist);
2726                 }
2727             }
2728             return canPersist;
2729         }
2730 
2731         private void validateJobFlags(JobInfo job, int callingUid) {
2732             job.enforceValidity();
2733             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2734                 getContext().enforceCallingOrSelfPermission(
2735                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2736             }
2737             if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2738                 if (callingUid != Process.SYSTEM_UID) {
2739                     throw new SecurityException("Job has invalid flags");
2740                 }
2741                 if (job.isPeriodic()) {
2742                     Slog.wtf(TAG, "Periodic jobs mustn't have"
2743                             + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
2744                 }
2745             }
2746         }
2747 
2748         // IJobScheduler implementation
2749         @Override
2750         public int schedule(JobInfo job) throws RemoteException {
2751             if (DEBUG) {
2752                 Slog.d(TAG, "Scheduling job: " + job.toString());
2753             }
2754             final int pid = Binder.getCallingPid();
2755             final int uid = Binder.getCallingUid();
2756             final int userId = UserHandle.getUserId(uid);
2757 
2758             enforceValidJobRequest(uid, job);
2759             if (job.isPersisted()) {
2760                 if (!canPersistJobs(pid, uid)) {
2761                     throw new IllegalArgumentException("Error: requested job be persisted without"
2762                             + " holding RECEIVE_BOOT_COMPLETED permission.");
2763                 }
2764             }
2765 
2766             validateJobFlags(job, uid);
2767 
2768             final long ident = Binder.clearCallingIdentity();
2769             try {
2770                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2771                         null);
2772             } finally {
2773                 Binder.restoreCallingIdentity(ident);
2774             }
2775         }
2776 
2777         // IJobScheduler implementation
2778         @Override
2779         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2780             if (DEBUG) {
2781                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2782             }
2783             final int uid = Binder.getCallingUid();
2784             final int userId = UserHandle.getUserId(uid);
2785 
2786             enforceValidJobRequest(uid, job);
2787             if (job.isPersisted()) {
2788                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2789             }
2790             if (work == null) {
2791                 throw new NullPointerException("work is null");
2792             }
2793 
2794             validateJobFlags(job, uid);
2795 
2796             final long ident = Binder.clearCallingIdentity();
2797             try {
2798                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2799                         null);
2800             } finally {
2801                 Binder.restoreCallingIdentity(ident);
2802             }
2803         }
2804 
2805         @Override
2806         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
2807                 throws RemoteException {
2808             final int callerUid = Binder.getCallingUid();
2809             if (DEBUG) {
2810                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
2811                         + " on behalf of " + packageName + "/");
2812             }
2813 
2814             if (packageName == null) {
2815                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
2816             }
2817 
2818             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2819                     android.Manifest.permission.UPDATE_DEVICE_STATS);
2820             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2821                 throw new SecurityException("Caller uid " + callerUid
2822                         + " not permitted to schedule jobs for other apps");
2823             }
2824 
2825             validateJobFlags(job, callerUid);
2826 
2827             final long ident = Binder.clearCallingIdentity();
2828             try {
2829                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
2830                         packageName, userId, tag);
2831             } finally {
2832                 Binder.restoreCallingIdentity(ident);
2833             }
2834         }
2835 
2836         @Override
2837         public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
2838             final int uid = Binder.getCallingUid();
2839 
2840             final long ident = Binder.clearCallingIdentity();
2841             try {
2842                 return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
2843             } finally {
2844                 Binder.restoreCallingIdentity(ident);
2845             }
2846         }
2847 
2848         @Override
2849         public JobInfo getPendingJob(int jobId) throws RemoteException {
2850             final int uid = Binder.getCallingUid();
2851 
2852             final long ident = Binder.clearCallingIdentity();
2853             try {
2854                 return JobSchedulerService.this.getPendingJob(uid, jobId);
2855             } finally {
2856                 Binder.restoreCallingIdentity(ident);
2857             }
2858         }
2859 
2860         @Override
2861         public void cancelAll() throws RemoteException {
2862             final int uid = Binder.getCallingUid();
2863             final long ident = Binder.clearCallingIdentity();
2864             try {
2865                 JobSchedulerService.this.cancelJobsForUid(uid,
2866                         JobParameters.STOP_REASON_CANCELLED_BY_APP,
2867                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
2868                         "cancelAll() called by app, callingUid=" + uid);
2869             } finally {
2870                 Binder.restoreCallingIdentity(ident);
2871             }
2872         }
2873 
2874         @Override
2875         public void cancel(int jobId) throws RemoteException {
2876             final int uid = Binder.getCallingUid();
2877 
2878             final long ident = Binder.clearCallingIdentity();
2879             try {
2880                 JobSchedulerService.this.cancelJob(uid, jobId, uid,
2881                         JobParameters.STOP_REASON_CANCELLED_BY_APP);
2882             } finally {
2883                 Binder.restoreCallingIdentity(ident);
2884             }
2885         }
2886 
2887         /**
2888          * "dumpsys" infrastructure
2889          */
2890         @Override
2891         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2892             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2893 
2894             int filterUid = -1;
2895             boolean proto = false;
2896             if (!ArrayUtils.isEmpty(args)) {
2897                 int opti = 0;
2898                 while (opti < args.length) {
2899                     String arg = args[opti];
2900                     if ("-h".equals(arg)) {
2901                         dumpHelp(pw);
2902                         return;
2903                     } else if ("-a".equals(arg)) {
2904                         // Ignore, we always dump all.
2905                     } else if ("--proto".equals(arg)) {
2906                         proto = true;
2907                     } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2908                         pw.println("Unknown option: " + arg);
2909                         return;
2910                     } else {
2911                         break;
2912                     }
2913                     opti++;
2914                 }
2915                 if (opti < args.length) {
2916                     String pkg = args[opti];
2917                     try {
2918                         filterUid = getContext().getPackageManager().getPackageUid(pkg,
2919                                 PackageManager.MATCH_ANY_USER);
2920                     } catch (NameNotFoundException ignored) {
2921                         pw.println("Invalid package: " + pkg);
2922                         return;
2923                     }
2924                 }
2925             }
2926 
2927             final long identityToken = Binder.clearCallingIdentity();
2928             try {
2929                 if (proto) {
2930                     JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2931                 } else {
2932                     JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, "  "),
2933                             filterUid);
2934                 }
2935             } finally {
2936                 Binder.restoreCallingIdentity(identityToken);
2937             }
2938         }
2939 
2940         @Override
2941         public int handleShellCommand(@NonNull ParcelFileDescriptor in,
2942                 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
2943                 @NonNull String[] args) {
2944             return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2945                     this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
2946                     args);
2947         }
2948 
2949         /**
2950          * <b>For internal system user only!</b>
2951          * Returns a list of all currently-executing jobs.
2952          */
2953         @Override
2954         public List<JobInfo> getStartedJobs() {
2955             final int uid = Binder.getCallingUid();
2956             if (uid != Process.SYSTEM_UID) {
2957                 throw new SecurityException(
2958                     "getStartedJobs() is system internal use only.");
2959             }
2960 
2961             final ArrayList<JobInfo> runningJobs;
2962 
2963             synchronized (mLock) {
2964                 runningJobs = new ArrayList<>(mActiveServices.size());
2965                 for (JobServiceContext jsc : mActiveServices) {
2966                     final JobStatus job = jsc.getRunningJobLocked();
2967                     if (job != null) {
2968                         runningJobs.add(job.getJob());
2969                     }
2970                 }
2971             }
2972 
2973             return runningJobs;
2974         }
2975 
2976         /**
2977          * <b>For internal system user only!</b>
2978          * Returns a snapshot of the state of all jobs known to the system.
2979          *
2980          * <p class="note">This is a slow operation, so it should be called sparingly.
2981          */
2982         @Override
2983         public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
2984             final int uid = Binder.getCallingUid();
2985             if (uid != Process.SYSTEM_UID) {
2986                 throw new SecurityException(
2987                     "getAllJobSnapshots() is system internal use only.");
2988             }
2989             synchronized (mLock) {
2990                 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
2991                 mJobs.forEachJob((job) -> snapshots.add(
2992                         new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
2993                                 isReadyToBeExecutedLocked(job))));
2994                 return new ParceledListSlice<>(snapshots);
2995             }
2996         }
2997     }
2998 
2999     // Shell command infrastructure: run the given job immediately
3000     int executeRunCommand(String pkgName, int userId, int jobId, boolean satisfied, boolean force) {
3001         Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + userId
3002                 + " " + jobId + " s=" + satisfied + " f=" + force);
3003 
3004         try {
3005             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3006                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
3007             if (uid < 0) {
3008                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3009             }
3010 
3011             synchronized (mLock) {
3012                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3013                 if (js == null) {
3014                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3015                 }
3016 
3017                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL
3018                         : (satisfied ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_SOFT);
3019 
3020                 // Re-evaluate constraints after the override is set in case one of the overridden
3021                 // constraints was preventing another constraint from thinking it needed to update.
3022                 for (int c = mControllers.size() - 1; c >= 0; --c) {
3023                     mControllers.get(c).reevaluateStateLocked(uid);
3024                 }
3025 
3026                 if (!js.isConstraintsSatisfied()) {
3027                     js.overrideState = JobStatus.OVERRIDE_NONE;
3028                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
3029                 }
3030 
3031                 queueReadyJobsForExecutionLocked();
3032                 maybeRunPendingJobsLocked();
3033             }
3034         } catch (RemoteException e) {
3035             // can't happen
3036         }
3037         return 0;
3038     }
3039 
3040     // Shell command infrastructure: immediately timeout currently executing jobs
3041     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
3042             boolean hasJobId, int jobId) {
3043         if (DEBUG) {
3044             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
3045         }
3046 
3047         synchronized (mLock) {
3048             boolean foundSome = false;
3049             for (int i=0; i<mActiveServices.size(); i++) {
3050                 final JobServiceContext jc = mActiveServices.get(i);
3051                 final JobStatus js = jc.getRunningJobLocked();
3052                 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
3053                     foundSome = true;
3054                     pw.print("Timing out: ");
3055                     js.printUniqueId(pw);
3056                     pw.print(" ");
3057                     pw.println(js.getServiceComponent().flattenToShortString());
3058                 }
3059             }
3060             if (!foundSome) {
3061                 pw.println("No matching executing jobs found.");
3062             }
3063         }
3064         return 0;
3065     }
3066 
3067     // Shell command infrastructure: cancel a scheduled job
3068     int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
3069             boolean hasJobId, int jobId) {
3070         if (DEBUG) {
3071             Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
3072         }
3073 
3074         int pkgUid = -1;
3075         try {
3076             IPackageManager pm = AppGlobals.getPackageManager();
3077             pkgUid = pm.getPackageUid(pkgName, 0, userId);
3078         } catch (RemoteException e) { /* can't happen */ }
3079 
3080         if (pkgUid < 0) {
3081             pw.println("Package " + pkgName + " not found.");
3082             return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3083         }
3084 
3085         if (!hasJobId) {
3086             pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
3087             if (!cancelJobsForUid(pkgUid, JobParameters.STOP_REASON_USER,
3088                     JobParameters.INTERNAL_STOP_REASON_CANCELED,
3089                     "cancel shell command for package")) {
3090                 pw.println("No matching jobs found.");
3091             }
3092         } else {
3093             pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
3094             if (!cancelJob(pkgUid, jobId, Process.SHELL_UID, JobParameters.STOP_REASON_USER)) {
3095                 pw.println("No matching job found.");
3096             }
3097         }
3098 
3099         return 0;
3100     }
3101 
3102     void setMonitorBattery(boolean enabled) {
3103         synchronized (mLock) {
3104             if (mBatteryController != null) {
3105                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
3106             }
3107         }
3108     }
3109 
3110     int getBatterySeq() {
3111         synchronized (mLock) {
3112             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
3113         }
3114     }
3115 
3116     boolean getBatteryCharging() {
3117         synchronized (mLock) {
3118             return mBatteryController != null
3119                     ? mBatteryController.getTracker().isOnStablePower() : false;
3120         }
3121     }
3122 
3123     boolean getBatteryNotLow() {
3124         synchronized (mLock) {
3125             return mBatteryController != null
3126                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
3127         }
3128     }
3129 
3130     int getStorageSeq() {
3131         synchronized (mLock) {
3132             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
3133         }
3134     }
3135 
3136     boolean getStorageNotLow() {
3137         synchronized (mLock) {
3138             return mStorageController != null
3139                     ? mStorageController.getTracker().isStorageNotLow() : false;
3140         }
3141     }
3142 
3143     // Shell command infrastructure
3144     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
3145         try {
3146             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3147                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
3148             if (uid < 0) {
3149                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
3150                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3151             }
3152 
3153             synchronized (mLock) {
3154                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3155                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
3156                 if (js == null) {
3157                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
3158                     pw.print("/jid"); pw.print(jobId); pw.println(")");
3159                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3160                 }
3161 
3162                 boolean printed = false;
3163                 if (mPendingJobs.contains(js)) {
3164                     pw.print("pending");
3165                     printed = true;
3166                 }
3167                 if (mConcurrencyManager.isJobRunningLocked(js)) {
3168                     if (printed) {
3169                         pw.print(" ");
3170                     }
3171                     printed = true;
3172                     pw.println("active");
3173                 }
3174                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
3175                     if (printed) {
3176                         pw.print(" ");
3177                     }
3178                     printed = true;
3179                     pw.println("user-stopped");
3180                 }
3181                 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
3182                     if (printed) {
3183                         pw.print(" ");
3184                     }
3185                     printed = true;
3186                     pw.println("source-user-stopped");
3187                 }
3188                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
3189                     if (printed) {
3190                         pw.print(" ");
3191                     }
3192                     printed = true;
3193                     pw.println("backing-up");
3194                 }
3195                 boolean componentPresent = false;
3196                 try {
3197                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3198                             js.getServiceComponent(),
3199                             PackageManager.MATCH_DIRECT_BOOT_AUTO,
3200                             js.getUserId()) != null);
3201                 } catch (RemoteException e) {
3202                 }
3203                 if (!componentPresent) {
3204                     if (printed) {
3205                         pw.print(" ");
3206                     }
3207                     printed = true;
3208                     pw.println("no-component");
3209                 }
3210                 if (js.isReady()) {
3211                     if (printed) {
3212                         pw.print(" ");
3213                     }
3214                     printed = true;
3215                     pw.println("ready");
3216                 }
3217                 if (!printed) {
3218                     pw.print("waiting");
3219                 }
3220                 pw.println();
3221             }
3222         } catch (RemoteException e) {
3223             // can't happen
3224         }
3225         return 0;
3226     }
3227 
3228     void resetExecutionQuota(@NonNull String pkgName, int userId) {
3229         synchronized (mLock) {
3230             mQuotaController.clearAppStatsLocked(userId, pkgName);
3231         }
3232     }
3233 
3234     void resetScheduleQuota() {
3235         mQuotaTracker.clear();
3236     }
3237 
3238     void triggerDockState(boolean idleState) {
3239         final Intent dockIntent;
3240         if (idleState) {
3241             dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3242         } else {
3243             dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3244         }
3245         dockIntent.setPackage("android");
3246         dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3247         getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3248     }
3249 
3250     static void dumpHelp(PrintWriter pw) {
3251         pw.println("Job Scheduler (jobscheduler) dump options:");
3252         pw.println("  [-h] [package] ...");
3253         pw.println("    -h: print this help");
3254         pw.println("  [package] is an optional package name to limit the output to.");
3255     }
3256 
3257     /** Sort jobs by caller UID, then by Job ID. */
3258     private static void sortJobs(List<JobStatus> jobs) {
3259         Collections.sort(jobs, new Comparator<JobStatus>() {
3260             @Override
3261             public int compare(JobStatus o1, JobStatus o2) {
3262                 int uid1 = o1.getUid();
3263                 int uid2 = o2.getUid();
3264                 int id1 = o1.getJobId();
3265                 int id2 = o2.getJobId();
3266                 if (uid1 != uid2) {
3267                     return uid1 < uid2 ? -1 : 1;
3268                 }
3269                 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
3270             }
3271         });
3272     }
3273 
3274     void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
3275         final int filterAppId = UserHandle.getAppId(filterUid);
3276         final long now = sSystemClock.millis();
3277         final long nowElapsed = sElapsedRealtimeClock.millis();
3278         final long nowUptime = sUptimeMillisClock.millis();
3279 
3280         final Predicate<JobStatus> predicate = (js) -> {
3281             return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
3282                     || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
3283         };
3284         synchronized (mLock) {
3285             mConstants.dump(pw);
3286             for (StateController controller : mControllers) {
3287                 pw.increaseIndent();
3288                 controller.dumpConstants(pw);
3289                 pw.decreaseIndent();
3290             }
3291             pw.println();
3292 
3293             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3294                 mJobRestrictions.get(i).dumpConstants(pw);
3295             }
3296             pw.println();
3297 
3298             mQuotaTracker.dump(pw);
3299             pw.println();
3300 
3301             pw.println("Started users: " + Arrays.toString(mStartedUsers));
3302             pw.print("Registered ");
3303             pw.print(mJobs.size());
3304             pw.println(" jobs:");
3305             pw.increaseIndent();
3306             boolean jobPrinted = false;
3307             if (mJobs.size() > 0) {
3308                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3309                 sortJobs(jobs);
3310                 for (JobStatus job : jobs) {
3311                     // Skip printing details if the caller requested a filter
3312                     if (!predicate.test(job)) {
3313                         continue;
3314                     }
3315                     jobPrinted = true;
3316 
3317                     pw.print("JOB #"); job.printUniqueId(pw); pw.print(": ");
3318                     pw.println(job.toShortStringExceptUniqueId());
3319 
3320                     pw.increaseIndent();
3321                     job.dump(pw, true, nowElapsed);
3322 
3323                     pw.print("Restricted due to:");
3324                     final boolean isRestricted = checkIfRestricted(job) != null;
3325                     if (isRestricted) {
3326                         for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3327                             final JobRestriction restriction = mJobRestrictions.get(i);
3328                             if (restriction.isJobRestricted(job)) {
3329                                 final int reason = restriction.getInternalReason();
3330                                 pw.print(" ");
3331                                 pw.print(JobParameters.getInternalReasonCodeDescription(reason));
3332                             }
3333                         }
3334                     } else {
3335                         pw.print(" none");
3336                     }
3337                     pw.println(".");
3338 
3339                     pw.print("Ready: ");
3340                     pw.print(isReadyToBeExecutedLocked(job));
3341                     pw.print(" (job=");
3342                     pw.print(job.isReady());
3343                     pw.print(" user=");
3344                     pw.print(areUsersStartedLocked(job));
3345                     pw.print(" !restricted=");
3346                     pw.print(!isRestricted);
3347                     pw.print(" !pending=");
3348                     pw.print(!mPendingJobs.contains(job));
3349                     pw.print(" !active=");
3350                     pw.print(!mConcurrencyManager.isJobRunningLocked(job));
3351                     pw.print(" !backingup=");
3352                     pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
3353                     pw.print(" comp=");
3354                     pw.print(isComponentUsable(job));
3355                     pw.println(")");
3356 
3357                     pw.decreaseIndent();
3358                 }
3359             }
3360             if (!jobPrinted) {
3361                 pw.println("None.");
3362             }
3363             pw.decreaseIndent();
3364 
3365             for (int i=0; i<mControllers.size(); i++) {
3366                 pw.println();
3367                 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3368                 pw.increaseIndent();
3369                 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3370                 pw.decreaseIndent();
3371             }
3372 
3373             boolean overridePrinted = false;
3374             for (int i=0; i< mUidPriorityOverride.size(); i++) {
3375                 int uid = mUidPriorityOverride.keyAt(i);
3376                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
3377                     if (!overridePrinted) {
3378                         overridePrinted = true;
3379                         pw.println();
3380                         pw.println("Uid priority overrides:");
3381                         pw.increaseIndent();
3382                     }
3383                     pw.print(UserHandle.formatUid(uid));
3384                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3385                 }
3386             }
3387             if (overridePrinted) {
3388                 pw.decreaseIndent();
3389             }
3390 
3391             boolean uidMapPrinted = false;
3392             for (int i = 0; i < mUidToPackageCache.size(); ++i) {
3393                 final int uid = mUidToPackageCache.keyAt(i);
3394                 if (filterUid != -1 && filterUid != uid) {
3395                     continue;
3396                 }
3397                 if (!uidMapPrinted) {
3398                     uidMapPrinted = true;
3399                     pw.println();
3400                     pw.println("Cached UID->package map:");
3401                     pw.increaseIndent();
3402                 }
3403                 pw.print(uid);
3404                 pw.print(": ");
3405                 pw.println(mUidToPackageCache.get(uid));
3406             }
3407             if (uidMapPrinted) {
3408                 pw.decreaseIndent();
3409             }
3410 
3411             boolean backingPrinted = false;
3412             for (int i = 0; i < mBackingUpUids.size(); i++) {
3413                 int uid = mBackingUpUids.keyAt(i);
3414                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
3415                     if (!backingPrinted) {
3416                         pw.println();
3417                         pw.println("Backing up uids:");
3418                         pw.increaseIndent();
3419                         backingPrinted = true;
3420                     } else {
3421                         pw.print(", ");
3422                     }
3423                     pw.print(UserHandle.formatUid(uid));
3424                 }
3425             }
3426             if (backingPrinted) {
3427                 pw.decreaseIndent();
3428                 pw.println();
3429             }
3430 
3431             pw.println();
3432             mJobPackageTracker.dump(pw, filterAppId);
3433             pw.println();
3434             if (mJobPackageTracker.dumpHistory(pw, filterAppId)) {
3435                 pw.println();
3436             }
3437 
3438             boolean pendingPrinted = false;
3439             pw.println("Pending queue:");
3440             pw.increaseIndent();
3441             for (int i=0; i<mPendingJobs.size(); i++) {
3442                 JobStatus job = mPendingJobs.get(i);
3443                 if (!predicate.test(job)) {
3444                     continue;
3445                 }
3446                 if (!pendingPrinted) {
3447                     pendingPrinted = true;
3448                 }
3449 
3450                 pw.print("Pending #"); pw.print(i); pw.print(": ");
3451                 pw.println(job.toShortString());
3452 
3453                 pw.increaseIndent();
3454                 job.dump(pw, false, nowElapsed);
3455                 int priority = evaluateJobPriorityLocked(job);
3456                 pw.print("Evaluated priority: ");
3457                 pw.println(JobInfo.getPriorityString(priority));
3458 
3459                 pw.print("Tag: "); pw.println(job.getTag());
3460                 pw.print("Enq: ");
3461                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
3462                 pw.decreaseIndent();
3463                 pw.println();
3464             }
3465             if (!pendingPrinted) {
3466                 pw.println("None");
3467             }
3468             pw.decreaseIndent();
3469 
3470             pw.println();
3471             pw.println("Active jobs:");
3472             pw.increaseIndent();
3473             for (int i=0; i<mActiveServices.size(); i++) {
3474                 JobServiceContext jsc = mActiveServices.get(i);
3475                 final JobStatus job = jsc.getRunningJobLocked();
3476 
3477                 if (job != null && !predicate.test(job)) {
3478                     continue;
3479                 }
3480 
3481                 pw.print("Slot #"); pw.print(i); pw.print(": ");
3482                 jsc.dumpLocked(pw, nowElapsed);
3483 
3484                 if (job != null) {
3485                     pw.increaseIndent();
3486 
3487                     pw.increaseIndent();
3488                     job.dump(pw, false, nowElapsed);
3489                     pw.decreaseIndent();
3490 
3491                     pw.print("Evaluated priority: ");
3492                     pw.println(JobInfo.getPriorityString(job.lastEvaluatedPriority));
3493 
3494                     pw.print("Active at ");
3495                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
3496                     pw.print(", pending for ");
3497                     TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3498                     pw.decreaseIndent();
3499                     pw.println();
3500                 }
3501             }
3502             pw.decreaseIndent();
3503 
3504             pw.println();
3505             boolean recentPrinted = false;
3506             pw.println("Recently completed jobs:");
3507             pw.increaseIndent();
3508             for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) {
3509                 // Print most recent first
3510                 final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r)
3511                         % NUM_COMPLETED_JOB_HISTORY;
3512                 final JobStatus job = mLastCompletedJobs[idx];
3513                 if (job != null) {
3514                     if (!predicate.test(job)) {
3515                         continue;
3516                     }
3517                     recentPrinted = true;
3518                     TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw);
3519                     pw.println();
3520                     // Double indent for readability
3521                     pw.increaseIndent();
3522                     pw.increaseIndent();
3523                     job.dump(pw, true, nowElapsed);
3524                     pw.decreaseIndent();
3525                     pw.decreaseIndent();
3526                 }
3527             }
3528             if (!recentPrinted) {
3529                 pw.println("None");
3530             }
3531             pw.decreaseIndent();
3532             pw.println();
3533 
3534             if (filterUid == -1) {
3535                 pw.println();
3536                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3537                 pw.print("mReportedActive="); pw.println(mReportedActive);
3538             }
3539             pw.println();
3540 
3541             mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
3542 
3543             pw.println();
3544             pw.print("PersistStats: ");
3545             pw.println(mJobs.getPersistStats());
3546         }
3547         pw.println();
3548     }
3549 
3550     void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3551         ProtoOutputStream proto = new ProtoOutputStream(fd);
3552         final int filterAppId = UserHandle.getAppId(filterUid);
3553         final long now = sSystemClock.millis();
3554         final long nowElapsed = sElapsedRealtimeClock.millis();
3555         final long nowUptime = sUptimeMillisClock.millis();
3556         final Predicate<JobStatus> predicate = (js) -> {
3557             return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
3558                     || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
3559         };
3560 
3561         synchronized (mLock) {
3562             final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS);
3563             mConstants.dump(proto);
3564             for (StateController controller : mControllers) {
3565                 controller.dumpConstants(proto);
3566             }
3567             proto.end(settingsToken);
3568 
3569             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3570                 mJobRestrictions.get(i).dumpConstants(proto);
3571             }
3572 
3573             for (int u : mStartedUsers) {
3574                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3575             }
3576 
3577             mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER);
3578 
3579             if (mJobs.size() > 0) {
3580                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3581                 sortJobs(jobs);
3582                 for (JobStatus job : jobs) {
3583                     final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3584                     job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3585 
3586                     // Skip printing details if the caller requested a filter
3587                     if (!predicate.test(job)) {
3588                         continue;
3589                     }
3590 
3591                     job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3592 
3593                     proto.write(
3594                             JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
3595                             isReadyToBeExecutedLocked(job));
3596                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3597                             job.isReady());
3598                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
3599                             areUsersStartedLocked(job));
3600                     proto.write(
3601                             JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
3602                             checkIfRestricted(job) != null);
3603                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3604                             mPendingJobs.contains(job));
3605                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3606                             mConcurrencyManager.isJobRunningLocked(job));
3607                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3608                             mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3609                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
3610                             isComponentUsable(job));
3611 
3612                     for (JobRestriction restriction : mJobRestrictions) {
3613                         final long restrictionsToken = proto.start(
3614                                 JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
3615                         proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
3616                                 restriction.getInternalReason());
3617                         proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
3618                                 restriction.isJobRestricted(job));
3619                         proto.end(restrictionsToken);
3620                     }
3621 
3622                     proto.end(rjToken);
3623                 }
3624             }
3625             for (StateController controller : mControllers) {
3626                 controller.dumpControllerStateLocked(
3627                         proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
3628             }
3629             for (int i=0; i< mUidPriorityOverride.size(); i++) {
3630                 int uid = mUidPriorityOverride.keyAt(i);
3631                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
3632                     long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3633                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3634                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3635                             mUidPriorityOverride.valueAt(i));
3636                     proto.end(pToken);
3637                 }
3638             }
3639             for (int i = 0; i < mBackingUpUids.size(); i++) {
3640                 int uid = mBackingUpUids.keyAt(i);
3641                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
3642                     proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3643                 }
3644             }
3645 
3646             mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3647                     filterAppId);
3648             mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3649                     filterAppId);
3650 
3651             for (JobStatus job : mPendingJobs) {
3652                 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3653 
3654                 job.writeToShortProto(proto, PendingJob.INFO);
3655                 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3656                 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
3657                 proto.write(PendingJob.PENDING_DURATION_MS, nowUptime - job.madePending);
3658 
3659                 proto.end(pjToken);
3660             }
3661             for (JobServiceContext jsc : mActiveServices) {
3662                 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3663                 final JobStatus job = jsc.getRunningJobLocked();
3664 
3665                 if (job == null) {
3666                     final long ijToken = proto.start(ActiveJob.INACTIVE);
3667 
3668                         proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3669                                 nowElapsed - jsc.mStoppedTime);
3670                     if (jsc.mStoppedReason != null) {
3671                         proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3672                                 jsc.mStoppedReason);
3673                     }
3674 
3675                     proto.end(ijToken);
3676                 } else {
3677                     final long rjToken = proto.start(ActiveJob.RUNNING);
3678 
3679                     job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3680 
3681                     proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3682                             nowElapsed - jsc.getExecutionStartTimeElapsed());
3683                     proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3684                             jsc.getTimeoutElapsed() - nowElapsed);
3685 
3686                     job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3687 
3688                     proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
3689                             evaluateJobPriorityLocked(job));
3690 
3691                     proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3692                             nowUptime - job.madeActive);
3693                     proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3694                             job.madeActive - job.madePending);
3695 
3696                     proto.end(rjToken);
3697                 }
3698                 proto.end(ajToken);
3699             }
3700             if (filterUid == -1) {
3701                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3702                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3703             }
3704             mConcurrencyManager.dumpProtoLocked(proto,
3705                     JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
3706 
3707             mJobs.getPersistStats().dumpDebug(proto, JobSchedulerServiceDumpProto.PERSIST_STATS);
3708         }
3709 
3710         proto.flush();
3711     }
3712 }
3713