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