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.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
20 import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
21 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
22 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
23 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
24 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
25 
26 import android.Manifest;
27 import android.annotation.EnforcePermission;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.UserIdInt;
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.app.ActivityManagerInternal;
34 import android.app.AppGlobals;
35 import android.app.IUidObserver;
36 import android.app.UidObserver;
37 import android.app.compat.CompatChanges;
38 import android.app.job.IJobScheduler;
39 import android.app.job.IUserVisibleJobObserver;
40 import android.app.job.JobInfo;
41 import android.app.job.JobParameters;
42 import android.app.job.JobProtoEnums;
43 import android.app.job.JobScheduler;
44 import android.app.job.JobService;
45 import android.app.job.JobSnapshot;
46 import android.app.job.JobWorkItem;
47 import android.app.job.UserVisibleJobSummary;
48 import android.app.tare.EconomyManager;
49 import android.app.usage.UsageStatsManager;
50 import android.app.usage.UsageStatsManagerInternal;
51 import android.compat.annotation.ChangeId;
52 import android.compat.annotation.EnabledAfter;
53 import android.content.BroadcastReceiver;
54 import android.content.ComponentName;
55 import android.content.Context;
56 import android.content.Intent;
57 import android.content.IntentFilter;
58 import android.content.PermissionChecker;
59 import android.content.pm.ApplicationInfo;
60 import android.content.pm.IPackageManager;
61 import android.content.pm.PackageManager;
62 import android.content.pm.PackageManager.NameNotFoundException;
63 import android.content.pm.PackageManagerInternal;
64 import android.content.pm.ParceledListSlice;
65 import android.content.pm.ProviderInfo;
66 import android.content.pm.ServiceInfo;
67 import android.net.Network;
68 import android.net.Uri;
69 import android.os.BatteryManager;
70 import android.os.BatteryManagerInternal;
71 import android.os.BatteryStatsInternal;
72 import android.os.Binder;
73 import android.os.Build;
74 import android.os.Handler;
75 import android.os.LimitExceededException;
76 import android.os.Looper;
77 import android.os.Message;
78 import android.os.ParcelFileDescriptor;
79 import android.os.Process;
80 import android.os.RemoteCallbackList;
81 import android.os.RemoteException;
82 import android.os.SystemClock;
83 import android.os.UserHandle;
84 import android.os.WorkSource;
85 import android.os.storage.StorageManagerInternal;
86 import android.provider.DeviceConfig;
87 import android.provider.Settings;
88 import android.text.format.DateUtils;
89 import android.util.ArrayMap;
90 import android.util.ArraySet;
91 import android.util.IndentingPrintWriter;
92 import android.util.Log;
93 import android.util.Pair;
94 import android.util.Slog;
95 import android.util.SparseArray;
96 import android.util.SparseArrayMap;
97 import android.util.SparseBooleanArray;
98 import android.util.SparseIntArray;
99 import android.util.SparseSetArray;
100 import android.util.TimeUtils;
101 import android.util.proto.ProtoOutputStream;
102 
103 import com.android.internal.annotations.GuardedBy;
104 import com.android.internal.annotations.VisibleForTesting;
105 import com.android.internal.os.SomeArgs;
106 import com.android.internal.util.ArrayUtils;
107 import com.android.internal.util.DumpUtils;
108 import com.android.internal.util.FrameworkStatsLog;
109 import com.android.modules.expresslog.Counter;
110 import com.android.modules.expresslog.Histogram;
111 import com.android.server.AppSchedulingModuleThread;
112 import com.android.server.AppStateTracker;
113 import com.android.server.AppStateTrackerImpl;
114 import com.android.server.DeviceIdleInternal;
115 import com.android.server.LocalServices;
116 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
117 import com.android.server.job.controllers.BackgroundJobsController;
118 import com.android.server.job.controllers.BatteryController;
119 import com.android.server.job.controllers.ComponentController;
120 import com.android.server.job.controllers.ConnectivityController;
121 import com.android.server.job.controllers.ContentObserverController;
122 import com.android.server.job.controllers.DeviceIdleJobsController;
123 import com.android.server.job.controllers.FlexibilityController;
124 import com.android.server.job.controllers.IdleController;
125 import com.android.server.job.controllers.JobStatus;
126 import com.android.server.job.controllers.PrefetchController;
127 import com.android.server.job.controllers.QuotaController;
128 import com.android.server.job.controllers.RestrictingController;
129 import com.android.server.job.controllers.StateController;
130 import com.android.server.job.controllers.StorageController;
131 import com.android.server.job.controllers.TareController;
132 import com.android.server.job.controllers.TimeController;
133 import com.android.server.job.restrictions.JobRestriction;
134 import com.android.server.job.restrictions.ThermalStatusRestriction;
135 import com.android.server.pm.UserManagerInternal;
136 import com.android.server.tare.EconomyManagerInternal;
137 import com.android.server.tare.JobSchedulerEconomicPolicy;
138 import com.android.server.usage.AppStandbyInternal;
139 import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
140 import com.android.server.utils.quota.Categorizer;
141 import com.android.server.utils.quota.Category;
142 import com.android.server.utils.quota.CountQuotaTracker;
143 
144 import dalvik.annotation.optimization.NeverCompile;
145 
146 import libcore.util.EmptyArray;
147 
148 import java.io.FileDescriptor;
149 import java.io.PrintWriter;
150 import java.time.Clock;
151 import java.time.Instant;
152 import java.time.ZoneId;
153 import java.time.ZoneOffset;
154 import java.util.ArrayList;
155 import java.util.Arrays;
156 import java.util.Collections;
157 import java.util.Comparator;
158 import java.util.List;
159 import java.util.Map;
160 import java.util.Objects;
161 import java.util.concurrent.CountDownLatch;
162 import java.util.function.Consumer;
163 import java.util.function.Predicate;
164 
165 /**
166  * Responsible for taking jobs representing work to be performed by a client app, and determining
167  * based on the criteria specified when that job should be run against the client application's
168  * endpoint.
169  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
170  * about constraints, or the state of active jobs. It receives callbacks from the various
171  * controllers and completed jobs and operates accordingly.
172  *
173  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
174  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
175  *
176  * @hide
177  */
178 public class JobSchedulerService extends com.android.server.SystemService
179         implements StateChangedListener, JobCompletedListener {
180     public static final String TAG = "JobScheduler";
181     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
182     public static final boolean DEBUG_STANDBY = DEBUG || false;
183 
184     /** The maximum number of jobs that we allow an app to schedule */
185     private static final int MAX_JOBS_PER_APP = 150;
186     /** The number of the most recently completed jobs to keep track of for debugging purposes. */
187     private static final int NUM_COMPLETED_JOB_HISTORY = 20;
188 
189     /**
190      * Require the hosting job to specify a network constraint if the included
191      * {@link android.app.job.JobWorkItem} indicates network usage.
192      */
193     @ChangeId
194     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
195     private static final long REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS = 241104082L;
196 
197     /**
198      * Require the app to have the ACCESS_NETWORK_STATE permissions when scheduling
199      * a job with a connectivity constraint.
200      */
201     @ChangeId
202     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
203     static final long REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS = 271850009L;
204 
205     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
206     public static Clock sSystemClock = Clock.systemUTC();
207 
208     private abstract static class MySimpleClock extends Clock {
209         private final ZoneId mZoneId;
210 
MySimpleClock(ZoneId zoneId)211         MySimpleClock(ZoneId zoneId) {
212             this.mZoneId = zoneId;
213         }
214 
215         @Override
getZone()216         public ZoneId getZone() {
217             return mZoneId;
218         }
219 
220         @Override
withZone(ZoneId zone)221         public Clock withZone(ZoneId zone) {
222             return new MySimpleClock(zone) {
223                 @Override
224                 public long millis() {
225                     return MySimpleClock.this.millis();
226                 }
227             };
228         }
229 
230         @Override
millis()231         public abstract long millis();
232 
233         @Override
instant()234         public Instant instant() {
235             return Instant.ofEpochMilli(millis());
236         }
237     }
238 
239     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
240     public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) {
241         @Override
242         public long millis() {
243             return SystemClock.uptimeMillis();
244         }
245     };
246 
247     public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
248         @Override
249         public long millis() {
250             return SystemClock.elapsedRealtime();
251         }
252     };
253 
254     /** Global local for all job scheduler state. */
255     final Object mLock = new Object();
256     /** Master list of jobs. */
257     final JobStore mJobs;
258     private final CountDownLatch mJobStoreLoadedLatch;
259     /** Tracking the standby bucket state of each app */
260     final StandbyTracker mStandbyTracker;
261     /** Tracking amount of time each package runs for. */
262     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
263     final JobConcurrencyManager mConcurrencyManager;
264 
265     static final int MSG_CHECK_INDIVIDUAL_JOB = 0;
266     static final int MSG_CHECK_JOB = 1;
267     static final int MSG_STOP_JOB = 2;
268     static final int MSG_CHECK_JOB_GREEDY = 3;
269     static final int MSG_UID_STATE_CHANGED = 4;
270     static final int MSG_UID_GONE = 5;
271     static final int MSG_UID_ACTIVE = 6;
272     static final int MSG_UID_IDLE = 7;
273     static final int MSG_CHECK_CHANGED_JOB_LIST = 8;
274     static final int MSG_CHECK_MEDIA_EXEMPTION = 9;
275     static final int MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS = 10;
276     static final int MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE = 11;
277 
278     /** List of controllers that will notify this service of updates to jobs. */
279     final List<StateController> mControllers;
280     /**
281      * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of
282      * {@link #mControllers}.
283      */
284     private final List<RestrictingController> mRestrictiveControllers;
285     /** Need direct access to this for testing. */
286     private final StorageController mStorageController;
287     /** Needed to get estimated transfer time. */
288     private final ConnectivityController mConnectivityController;
289     /** Need directly for sending uid state changes */
290     private final DeviceIdleJobsController mDeviceIdleJobsController;
291     /** Needed to get next estimated launch time. */
292     private final PrefetchController mPrefetchController;
293     /** Needed to get remaining quota time. */
294     private final QuotaController mQuotaController;
295     /** Needed to get max execution time and expedited-job allowance. */
296     private final TareController mTareController;
297     /**
298      * List of restrictions.
299      * Note: do not add to or remove from this list at runtime except in the constructor, because we
300      * do not synchronize access to this list.
301      */
302     private final List<JobRestriction> mJobRestrictions;
303 
304     @GuardedBy("mLock")
305     private final BatteryStateTracker mBatteryStateTracker;
306 
307     @GuardedBy("mLock")
308     private final SparseArray<String> mCloudMediaProviderPackages = new SparseArray<>();
309 
310     private final RemoteCallbackList<IUserVisibleJobObserver> mUserVisibleJobObservers =
311             new RemoteCallbackList<>();
312 
313     /**
314      * Cache of grant status of permissions, keyed by UID->PID->permission name. A missing value
315      * means the state has not been queried.
316      */
317     @GuardedBy("mPermissionCache")
318     private final SparseArray<SparseArrayMap<String, Boolean>> mPermissionCache =
319             new SparseArray<>();
320 
321     private final CountQuotaTracker mQuotaTracker;
322     private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
323     private static final String QUOTA_TRACKER_SCHEDULE_LOGGED =
324             ".schedulePersisted out-of-quota logged";
325     private static final String QUOTA_TRACKER_TIMEOUT_UIJ_TAG = "timeout-uij";
326     private static final String QUOTA_TRACKER_TIMEOUT_EJ_TAG = "timeout-ej";
327     private static final String QUOTA_TRACKER_TIMEOUT_REG_TAG = "timeout-reg";
328     private static final String QUOTA_TRACKER_TIMEOUT_TOTAL_TAG = "timeout-total";
329     private static final String QUOTA_TRACKER_ANR_TAG = "anr";
330     private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED = new Category(
331             ".schedulePersisted()");
332     private static final Category QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED = new Category(
333             ".schedulePersisted out-of-quota logged");
334     private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ =
335             new Category(QUOTA_TRACKER_TIMEOUT_UIJ_TAG);
336     private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ =
337             new Category(QUOTA_TRACKER_TIMEOUT_EJ_TAG);
338     private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_REG =
339             new Category(QUOTA_TRACKER_TIMEOUT_REG_TAG);
340     private static final Category QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL =
341             new Category(QUOTA_TRACKER_TIMEOUT_TOTAL_TAG);
342     private static final Category QUOTA_TRACKER_CATEGORY_ANR = new Category(QUOTA_TRACKER_ANR_TAG);
343     private static final Category QUOTA_TRACKER_CATEGORY_DISABLED = new Category("disabled");
344 
345     /**
346      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
347      * when ready to execute them.
348      */
349     private final PendingJobQueue mPendingJobQueue = new PendingJobQueue();
350 
351     int[] mStartedUsers = EmptyArray.INT;
352 
353     final JobHandler mHandler;
354     final JobSchedulerStub mJobSchedulerStub;
355 
356     PackageManagerInternal mLocalPM;
357     ActivityManagerInternal mActivityManagerInternal;
358     DeviceIdleInternal mLocalDeviceIdleController;
359     @VisibleForTesting
360     AppStateTrackerImpl mAppStateTracker;
361     final UsageStatsManagerInternal mUsageStats;
362     private final AppStandbyInternal mAppStandbyInternal;
363 
364     /**
365      * Set to true once we are allowed to run third party apps.
366      */
367     boolean mReadyToRock;
368 
369     /**
370      * What we last reported to DeviceIdleController about whether we are active.
371      */
372     boolean mReportedActive;
373 
374     /**
375      * Track the most recently completed jobs (that had been executing and were stopped for any
376      * reason, including successful completion).
377      */
378     private int mLastCompletedJobIndex = 0;
379     private final JobStatus[] mLastCompletedJobs = new JobStatus[NUM_COMPLETED_JOB_HISTORY];
380     private final long[] mLastCompletedJobTimeElapsed = new long[NUM_COMPLETED_JOB_HISTORY];
381 
382     /**
383      * Track the most recently cancelled jobs (that had internal reason
384      * {@link JobParameters#INTERNAL_STOP_REASON_CANCELED}.
385      */
386     private int mLastCancelledJobIndex = 0;
387     private final JobStatus[] mLastCancelledJobs =
388             new JobStatus[DEBUG ? NUM_COMPLETED_JOB_HISTORY : 0];
389     private final long[] mLastCancelledJobTimeElapsed =
390             new long[DEBUG ? NUM_COMPLETED_JOB_HISTORY : 0];
391 
392     private static final Histogram sEnqueuedJwiHighWaterMarkLogger = new Histogram(
393             "job_scheduler.value_hist_w_uid_enqueued_work_items_high_water_mark",
394             new Histogram.ScaledRangeOptions(25, 0, 5, 1.4f));
395     private static final Histogram sInitialJobEstimatedNetworkDownloadKBLogger = new Histogram(
396             "job_scheduler.value_hist_initial_job_estimated_network_download_kilobytes",
397             new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
398     private static final Histogram sInitialJwiEstimatedNetworkDownloadKBLogger = new Histogram(
399             "job_scheduler.value_hist_initial_jwi_estimated_network_download_kilobytes",
400             new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
401     private static final Histogram sInitialJobEstimatedNetworkUploadKBLogger = new Histogram(
402             "job_scheduler.value_hist_initial_job_estimated_network_upload_kilobytes",
403             new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
404     private static final Histogram sInitialJwiEstimatedNetworkUploadKBLogger = new Histogram(
405             "job_scheduler.value_hist_initial_jwi_estimated_network_upload_kilobytes",
406             new Histogram.ScaledRangeOptions(50, 0, 32 /* 32 KB */, 1.31f));
407     private static final Histogram sJobMinimumChunkKBLogger = new Histogram(
408             "job_scheduler.value_hist_w_uid_job_minimum_chunk_kilobytes",
409             new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f));
410     private static final Histogram sJwiMinimumChunkKBLogger = new Histogram(
411             "job_scheduler.value_hist_w_uid_jwi_minimum_chunk_kilobytes",
412             new Histogram.ScaledRangeOptions(25, 0, 5 /* 5 KB */, 1.76f));
413 
414     /**
415      * A mapping of which uids are currently in the foreground to their effective bias.
416      */
417     final SparseIntArray mUidBiasOverride = new SparseIntArray();
418     /**
419      * A cached mapping of uids to their current capabilities.
420      */
421     @GuardedBy("mLock")
422     private final SparseIntArray mUidCapabilities = new SparseIntArray();
423     /**
424      * A cached mapping of uids to their proc states.
425      */
426     @GuardedBy("mLock")
427     private final SparseIntArray mUidProcStates = new SparseIntArray();
428 
429     /**
430      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
431      */
432     private final SparseBooleanArray mBackingUpUids = new SparseBooleanArray();
433 
434     /**
435      * Cache of debuggable app status.
436      */
437     final ArrayMap<String, Boolean> mDebuggableApps = new ArrayMap<>();
438 
439     /** Cached mapping of UIDs (for all users) to a list of packages in the UID. */
440     private final SparseSetArray<String> mUidToPackageCache = new SparseSetArray<>();
441 
442     /** List of jobs whose controller state has changed since the last time we evaluated the job. */
443     @GuardedBy("mLock")
444     private final ArraySet<JobStatus> mChangedJobList = new ArraySet<>();
445 
446     /**
447      * Cached pending job reasons. Mapping from UID -> namespace -> job ID -> reason.
448      */
449     @GuardedBy("mPendingJobReasonCache") // Use its own lock to avoid blocking JS processing
450     private final SparseArrayMap<String, SparseIntArray> mPendingJobReasonCache =
451             new SparseArrayMap<>();
452 
453     /**
454      * Named indices into standby bucket arrays, for clarity in referring to
455      * specific buckets' bookkeeping.
456      */
457     public static final int ACTIVE_INDEX = 0;
458     public static final int WORKING_INDEX = 1;
459     public static final int FREQUENT_INDEX = 2;
460     public static final int RARE_INDEX = 3;
461     public static final int NEVER_INDEX = 4;
462     // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
463     // (ScheduledJobStateChanged and JobStatusDumpProto).
464     public static final int RESTRICTED_INDEX = 5;
465     // Putting EXEMPTED_INDEX after RESTRICTED_INDEX to make it easier for proto dumping
466     // (ScheduledJobStateChanged and JobStatusDumpProto).
467     public static final int EXEMPTED_INDEX = 6;
468 
469     private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
470             EconomyManagerInternal.TareStateChangeListener {
471         public void start() {
472             DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
473                     AppSchedulingModuleThread.getExecutor(), this);
474             final EconomyManagerInternal economyManagerInternal =
475                     LocalServices.getService(EconomyManagerInternal.class);
476             economyManagerInternal
477                     .registerTareStateChangeListener(this, JobSchedulerEconomicPolicy.POLICY_JOB);
478             // Load all the constants.
479             synchronized (mLock) {
480                 mConstants.updateTareSettingsLocked(
481                         economyManagerInternal.getEnabledMode(
482                                 JobSchedulerEconomicPolicy.POLICY_JOB));
483             }
484             onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
485         }
486 
487         @Override
488         public void onPropertiesChanged(DeviceConfig.Properties properties) {
489             boolean apiQuotaScheduleUpdated = false;
490             boolean concurrencyUpdated = false;
491             boolean persistenceUpdated = false;
492             boolean runtimeUpdated = false;
493             for (int controller = 0; controller < mControllers.size(); controller++) {
494                 final StateController sc = mControllers.get(controller);
495                 sc.prepareForUpdatedConstantsLocked();
496             }
497 
498             synchronized (mLock) {
499                 for (String name : properties.getKeyset()) {
500                     if (name == null) {
501                         continue;
502                     }
503                     switch (name) {
504                         case Constants.KEY_ENABLE_API_QUOTAS:
505                         case Constants.KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC:
506                         case Constants.KEY_API_QUOTA_SCHEDULE_COUNT:
507                         case Constants.KEY_API_QUOTA_SCHEDULE_WINDOW_MS:
508                         case Constants.KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT:
509                         case Constants.KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION:
510                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT:
511                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT:
512                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT:
513                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT:
514                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS:
515                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT:
516                         case Constants.KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS:
517                             if (!apiQuotaScheduleUpdated) {
518                                 mConstants.updateApiQuotaConstantsLocked();
519                                 updateQuotaTracker();
520                                 apiQuotaScheduleUpdated = true;
521                             }
522                             break;
523                         case Constants.KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT:
524                         case Constants.KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS:
525                             mConstants.updateBatchingConstantsLocked();
526                             break;
527                         case Constants.KEY_HEAVY_USE_FACTOR:
528                         case Constants.KEY_MODERATE_USE_FACTOR:
529                             mConstants.updateUseFactorConstantsLocked();
530                             break;
531                         case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
532                         case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
533                         case Constants.KEY_SYSTEM_STOP_TO_FAILURE_RATIO:
534                             mConstants.updateBackoffConstantsLocked();
535                             break;
536                         case Constants.KEY_CONN_CONGESTION_DELAY_FRAC:
537                         case Constants.KEY_CONN_PREFETCH_RELAX_FRAC:
538                         case Constants.KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC:
539                         case Constants.KEY_CONN_USE_CELL_SIGNAL_STRENGTH:
540                         case Constants.KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS:
541                             mConstants.updateConnectivityConstantsLocked();
542                             break;
543                         case Constants.KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS:
544                             mConstants.updatePrefetchConstantsLocked();
545                             break;
546                         case Constants.KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS:
547                         case Constants.KEY_RUNTIME_MIN_GUARANTEE_MS:
548                         case Constants.KEY_RUNTIME_MIN_EJ_GUARANTEE_MS:
549                         case Constants.KEY_RUNTIME_MIN_UI_GUARANTEE_MS:
550                         case Constants.KEY_RUNTIME_UI_LIMIT_MS:
551                         case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR:
552                         case Constants.KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS:
553                         case Constants.KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS:
554                         case Constants.KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS:
555                             if (!runtimeUpdated) {
556                                 mConstants.updateRuntimeConstantsLocked();
557                                 runtimeUpdated = true;
558                             }
559                             break;
560                         case Constants.KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS:
561                         case Constants.KEY_PERSIST_IN_SPLIT_FILES:
562                             if (!persistenceUpdated) {
563                                 mConstants.updatePersistingConstantsLocked();
564                                 mJobs.setUseSplitFiles(mConstants.PERSIST_IN_SPLIT_FILES);
565                                 persistenceUpdated = true;
566                             }
567                             break;
568                         default:
569                             if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY)
570                                     && !concurrencyUpdated) {
571                                 mConcurrencyManager.updateConfigLocked();
572                                 concurrencyUpdated = true;
573                             } else {
574                                 for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) {
575                                     final StateController sc = mControllers.get(ctrlr);
576                                     sc.processConstantLocked(properties, name);
577                                 }
578                             }
579                             break;
580                     }
581                 }
582                 for (int controller = 0; controller < mControllers.size(); controller++) {
583                     final StateController sc = mControllers.get(controller);
584                     sc.onConstantsUpdatedLocked();
585                 }
586             }
587         }
588 
589         @Override
590         public void onTareEnabledModeChanged(@EconomyManager.EnabledMode int enabledMode) {
591             if (mConstants.updateTareSettingsLocked(enabledMode)) {
592                 for (int controller = 0; controller < mControllers.size(); controller++) {
593                     final StateController sc = mControllers.get(controller);
594                     sc.onConstantsUpdatedLocked();
595                 }
596                 onControllerStateChanged(null);
597             }
598         }
599     }
600 
601     @VisibleForTesting
602     void updateQuotaTracker() {
603         mQuotaTracker.setEnabled(
604                 mConstants.ENABLE_API_QUOTAS || mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC);
605         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED,
606                 mConstants.API_QUOTA_SCHEDULE_COUNT,
607                 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
608         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ,
609                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT,
610                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS);
611         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ,
612                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT,
613                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS);
614         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_REG,
615                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT,
616                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS);
617         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL,
618                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT,
619                 mConstants.EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS);
620         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_ANR,
621                 mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_COUNT,
622                 mConstants.EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS);
623     }
624 
625     /**
626      * All times are in milliseconds. Any access to this class or its fields should be done while
627      * holding the JobSchedulerService.mLock lock.
628      */
629     public static class Constants {
630         // Key names stored in the settings value.
631         private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
632                 "min_ready_non_active_jobs_count";
633         private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
634                 "max_non_active_job_batch_delay_ms";
635         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
636         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
637 
638         private static final String KEY_MIN_LINEAR_BACKOFF_TIME_MS = "min_linear_backoff_time_ms";
639         private static final String KEY_MIN_EXP_BACKOFF_TIME_MS = "min_exp_backoff_time_ms";
640         private static final String KEY_SYSTEM_STOP_TO_FAILURE_RATIO =
641                 "system_stop_to_failure_ratio";
642         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
643         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
644         private static final String KEY_CONN_USE_CELL_SIGNAL_STRENGTH =
645                 "conn_use_cell_signal_strength";
646         private static final String KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS =
647                 "conn_update_all_jobs_min_interval_ms";
648         private static final String KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC =
649                 "conn_low_signal_strength_relax_frac";
650         private static final String KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS =
651                 "prefetch_force_batch_relax_threshold_ms";
652         // This has been enabled for 3+ full releases. We're unlikely to disable it.
653         // TODO(141645789): remove this flag
654         private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas";
655         private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count";
656         private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
657         private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
658                 "aq_schedule_throw_exception";
659         private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
660                 "aq_schedule_return_failure";
661         private static final String KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC =
662                 "enable_execution_safeguards_udc";
663         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT =
664                 "es_u_timeout_uij_count";
665         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT =
666                 "es_u_timeout_ej_count";
667         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT =
668                 "es_u_timeout_reg_count";
669         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT =
670                 "es_u_timeout_total_count";
671         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS =
672                 "es_u_timeout_window_ms";
673         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT =
674                 "es_u_anr_count";
675         private static final String KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS =
676                 "es_u_anr_window_ms";
677 
678         private static final String KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS =
679                 "runtime_free_quota_max_limit_ms";
680         private static final String KEY_RUNTIME_MIN_GUARANTEE_MS = "runtime_min_guarantee_ms";
681         private static final String KEY_RUNTIME_MIN_EJ_GUARANTEE_MS = "runtime_min_ej_guarantee_ms";
682         private static final String KEY_RUNTIME_MIN_UI_GUARANTEE_MS = "runtime_min_ui_guarantee_ms";
683         private static final String KEY_RUNTIME_UI_LIMIT_MS = "runtime_ui_limit_ms";
684         private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
685                 "runtime_min_ui_data_transfer_guarantee_buffer_factor";
686         private static final String KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
687                 "runtime_min_ui_data_transfer_guarantee_ms";
688         private static final String KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS =
689                 "runtime_cumulative_ui_limit_ms";
690         private static final String KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS =
691                 "runtime_use_data_estimates_for_limits";
692 
693         private static final String KEY_PERSIST_IN_SPLIT_FILES = "persist_in_split_files";
694 
695         private static final String KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS =
696                 "max_num_persisted_job_work_items";
697 
698         private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
699         private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
700         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
701         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
702         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
703         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME_MS = JobInfo.MIN_BACKOFF_MILLIS;
704         private static final int DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO = 3;
705         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
706         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
707         private static final boolean DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH = true;
708         private static final long DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = MINUTE_IN_MILLIS;
709         private static final float DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = 0.5f;
710         private static final long DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = HOUR_IN_MILLIS;
711         private static final boolean DEFAULT_ENABLE_API_QUOTAS = true;
712         private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250;
713         private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
714         private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
715         private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
716         private static final boolean DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC = true;
717         private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = 2;
718         // EJs have a shorter timeout, so set a higher limit for them to start with.
719         private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = 5;
720         private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = 3;
721         private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = 10;
722         private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS =
723                 24 * HOUR_IN_MILLIS;
724         private static final int DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = 3;
725         private static final long DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS =
726                 6 * HOUR_IN_MILLIS;
727         @VisibleForTesting
728         public static final long DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = 30 * MINUTE_IN_MILLIS;
729         @VisibleForTesting
730         public static final long DEFAULT_RUNTIME_MIN_GUARANTEE_MS = 10 * MINUTE_IN_MILLIS;
731         @VisibleForTesting
732         public static final long DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS = 3 * MINUTE_IN_MILLIS;
733         public static final long DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS =
734                 Math.max(6 * HOUR_IN_MILLIS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS);
735         public static final long DEFAULT_RUNTIME_UI_LIMIT_MS =
736                 Math.max(12 * HOUR_IN_MILLIS, DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS);
737         public static final float DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
738                 1.35f;
739         public static final long DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
740                 Math.max(10 * MINUTE_IN_MILLIS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS);
741         public static final long DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS = 24 * HOUR_IN_MILLIS;
742         public static final boolean DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = false;
743         static final boolean DEFAULT_PERSIST_IN_SPLIT_FILES = true;
744         static final int DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS = 100_000;
745 
746         /**
747          * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
748          */
749         int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT;
750 
751         /**
752          * Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for
753          * at least this amount of time.
754          */
755         long MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
756 
757         /**
758          * This is the job execution factor that is considered to be heavy use of the system.
759          */
760         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
761         /**
762          * This is the job execution factor that is considered to be moderate use of the system.
763          */
764         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
765 
766         /**
767          * The minimum backoff time to allow for linear backoff.
768          */
769         long MIN_LINEAR_BACKOFF_TIME_MS = DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS;
770         /**
771          * The minimum backoff time to allow for exponential backoff.
772          */
773         long MIN_EXP_BACKOFF_TIME_MS = DEFAULT_MIN_EXP_BACKOFF_TIME_MS;
774         /**
775          * The ratio to use to convert number of times a job was stopped by JobScheduler to an
776          * incremental failure in the backoff policy calculation.
777          */
778         int SYSTEM_STOP_TO_FAILURE_RATIO = DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO;
779 
780         /**
781          * The fraction of a job's running window that must pass before we
782          * consider running it when the network is congested.
783          */
784         public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
785         /**
786          * The fraction of a prefetch job's running window that must pass before
787          * we consider matching it against a metered network.
788          */
789         public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
790         /**
791          * Whether to use the cell signal strength to determine if a particular job is eligible to
792          * run.
793          */
794         public boolean CONN_USE_CELL_SIGNAL_STRENGTH = DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH;
795         /**
796          * When throttling updating all tracked jobs, make sure not to update them more frequently
797          * than this value.
798          */
799         public long CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS =
800                 DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS;
801         /**
802          * The fraction of a job's running window that must pass before we consider running it on
803          * low signal strength networks.
804          */
805         public float CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC =
806                 DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC;
807 
808         /**
809          * The amount of time within which we would consider the app to be launching relatively soon
810          * and will relax the force batching policy on prefetch jobs. If the app is not going to be
811          * launched within this amount of time from now, then we will force batch the prefetch job.
812          */
813         public long PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS =
814                 DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS;
815 
816         /**
817          * Whether to enable quota limits on APIs.
818          */
819         public boolean ENABLE_API_QUOTAS = DEFAULT_ENABLE_API_QUOTAS;
820         /**
821          * The maximum number of schedule() calls an app can make in a set amount of time.
822          */
823         public int API_QUOTA_SCHEDULE_COUNT = DEFAULT_API_QUOTA_SCHEDULE_COUNT;
824         /**
825          * The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over.
826          */
827         public long API_QUOTA_SCHEDULE_WINDOW_MS = DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS;
828         /**
829          * Whether to throw an exception when an app hits its schedule quota limit.
830          */
831         public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION =
832                 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION;
833         /**
834          * Whether or not to return a failure result when an app hits its schedule quota limit.
835          */
836         public boolean API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
837                 DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT;
838 
839         /**
840          * Whether to enable the execution safeguards added in UDC.
841          */
842         public boolean ENABLE_EXECUTION_SAFEGUARDS_UDC = DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC;
843         /**
844          * The maximum number of times an app can have a user-iniated job time out before the system
845          * begins removing some of the app's privileges.
846          */
847         public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT =
848                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT;
849         /**
850          * The maximum number of times an app can have an expedited job time out before the system
851          * begins removing some of the app's privileges.
852          */
853         public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT =
854                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT;
855         /**
856          * The maximum number of times an app can have a regular job time out before the system
857          * begins removing some of the app's privileges.
858          */
859         public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT =
860                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT;
861         /**
862          * The maximum number of times an app can have jobs time out before the system
863          * attempts to restrict most of the app's privileges.
864          */
865         public int EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT =
866                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT;
867         /**
868          * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT},
869          * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT},
870          * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT}, and
871          * {@link #EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT} should be evaluated over.
872          */
873         public long EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS =
874                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS;
875 
876         /**
877          * The maximum number of times an app can ANR from JobScheduler's perspective before
878          * JobScheduler will attempt to restrict the app.
879          */
880         public int EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT;
881         /**
882          * The time window that {@link #EXECUTION_SAFEGUARDS_UDC_ANR_COUNT}
883          * should be evaluated over.
884          */
885         public long EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS =
886                 DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS;
887 
888         /** The maximum amount of time we will let a job run for when quota is "free". */
889         public long RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
890 
891         /**
892          * The minimum amount of time we try to guarantee regular jobs will run for.
893          */
894         public long RUNTIME_MIN_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_GUARANTEE_MS;
895 
896         /**
897          * The minimum amount of time we try to guarantee EJs will run for.
898          */
899         public long RUNTIME_MIN_EJ_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS;
900 
901         /**
902          * The minimum amount of time we try to guarantee normal user-initiated jobs will run for.
903          */
904         public long RUNTIME_MIN_UI_GUARANTEE_MS = DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS;
905 
906         /**
907          * The maximum amount of time we will let a user-initiated job run for. This will only
908          * apply if there are no other limits that apply to the specific user-initiated job.
909          */
910         public long RUNTIME_UI_LIMIT_MS = DEFAULT_RUNTIME_UI_LIMIT_MS;
911 
912         /**
913          * A factor to apply to estimated transfer durations for user-initiated data transfer jobs
914          * so that we give some extra time for unexpected situations. This will be at least 1 and
915          * so can just be multiplied with the original value to get the final value.
916          */
917         public float RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR =
918                 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR;
919 
920         /**
921          * The minimum amount of time we try to guarantee user-initiated data transfer jobs
922          * will run for. This is only considered when using data estimates to calculate
923          * execution limits.
924          */
925         public long RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS =
926                 DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS;
927 
928         /**
929          * The maximum amount of cumulative time we will let a user-initiated job run for
930          * before downgrading it.
931          */
932         public long RUNTIME_CUMULATIVE_UI_LIMIT_MS = DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS;
933 
934         /**
935          * Whether to use data estimates to determine execution limits for execution limits.
936          */
937         public boolean RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS =
938                 DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS;
939 
940         /**
941          * Whether to persist jobs in split files (by UID). If false, all persisted jobs will be
942          * saved in a single file.
943          */
944         public boolean PERSIST_IN_SPLIT_FILES = DEFAULT_PERSIST_IN_SPLIT_FILES;
945 
946         /**
947          * The maximum number of {@link JobWorkItem JobWorkItems} that can be persisted per job.
948          */
949         public int MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS;
950 
951         /**
952          * If true, use TARE policy for job limiting. If false, use quotas.
953          */
954         public boolean USE_TARE_POLICY = EconomyManager.DEFAULT_ENABLE_POLICY_JOB_SCHEDULER
955                 && EconomyManager.DEFAULT_ENABLE_TARE_MODE == EconomyManager.ENABLED_MODE_ON;
956 
957         private void updateBatchingConstantsLocked() {
958             MIN_READY_NON_ACTIVE_JOBS_COUNT = DeviceConfig.getInt(
959                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
960                     KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
961                     DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
962             MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DeviceConfig.getLong(
963                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
964                     KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
965                     DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
966         }
967 
968         private void updateUseFactorConstantsLocked() {
969             HEAVY_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
970                     KEY_HEAVY_USE_FACTOR,
971                     DEFAULT_HEAVY_USE_FACTOR);
972             MODERATE_USE_FACTOR = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
973                     KEY_MODERATE_USE_FACTOR,
974                     DEFAULT_MODERATE_USE_FACTOR);
975         }
976 
977         private void updateBackoffConstantsLocked() {
978             MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
979                     KEY_MIN_LINEAR_BACKOFF_TIME_MS,
980                     DEFAULT_MIN_LINEAR_BACKOFF_TIME_MS);
981             MIN_EXP_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
982                     KEY_MIN_EXP_BACKOFF_TIME_MS,
983                     DEFAULT_MIN_EXP_BACKOFF_TIME_MS);
984             SYSTEM_STOP_TO_FAILURE_RATIO = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
985                     KEY_SYSTEM_STOP_TO_FAILURE_RATIO,
986                     DEFAULT_SYSTEM_STOP_TO_FAILURE_RATIO);
987         }
988 
989         private void updateConnectivityConstantsLocked() {
990             CONN_CONGESTION_DELAY_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
991                     KEY_CONN_CONGESTION_DELAY_FRAC,
992                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
993             CONN_PREFETCH_RELAX_FRAC = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
994                     KEY_CONN_PREFETCH_RELAX_FRAC,
995                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
996             CONN_USE_CELL_SIGNAL_STRENGTH = DeviceConfig.getBoolean(
997                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
998                     KEY_CONN_USE_CELL_SIGNAL_STRENGTH,
999                     DEFAULT_CONN_USE_CELL_SIGNAL_STRENGTH);
1000             CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS = DeviceConfig.getLong(
1001                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1002                     KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS,
1003                     DEFAULT_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS);
1004             CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC = DeviceConfig.getFloat(
1005                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1006                     KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC,
1007                     DEFAULT_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC);
1008         }
1009 
1010         private void updatePersistingConstantsLocked() {
1011             PERSIST_IN_SPLIT_FILES = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1012                     KEY_PERSIST_IN_SPLIT_FILES, DEFAULT_PERSIST_IN_SPLIT_FILES);
1013             MAX_NUM_PERSISTED_JOB_WORK_ITEMS = DeviceConfig.getInt(
1014                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1015                     KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS,
1016                     DEFAULT_MAX_NUM_PERSISTED_JOB_WORK_ITEMS);
1017         }
1018 
1019         private void updatePrefetchConstantsLocked() {
1020             PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS = DeviceConfig.getLong(
1021                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1022                     KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS,
1023                     DEFAULT_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS);
1024         }
1025 
1026         private void updateApiQuotaConstantsLocked() {
1027             ENABLE_API_QUOTAS = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1028                     KEY_ENABLE_API_QUOTAS, DEFAULT_ENABLE_API_QUOTAS);
1029             ENABLE_EXECUTION_SAFEGUARDS_UDC = DeviceConfig.getBoolean(
1030                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1031                     KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, DEFAULT_ENABLE_EXECUTION_SAFEGUARDS_UDC);
1032             // Set a minimum value on the quota limit so it's not so low that it interferes with
1033             // legitimate use cases.
1034             API_QUOTA_SCHEDULE_COUNT = Math.max(250,
1035                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1036                             KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
1037             API_QUOTA_SCHEDULE_WINDOW_MS = DeviceConfig.getLong(
1038                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1039                     KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
1040             API_QUOTA_SCHEDULE_THROW_EXCEPTION = DeviceConfig.getBoolean(
1041                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1042                     KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
1043                     DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
1044             API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = DeviceConfig.getBoolean(
1045                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1046                     KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
1047                     DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
1048 
1049             // Set a minimum value on the timeout limit so it's not so low that it interferes with
1050             // legitimate use cases.
1051             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT = Math.max(2,
1052                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1053                             KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT,
1054                             DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT));
1055             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT = Math.max(2,
1056                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1057                             KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT,
1058                             DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT));
1059             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT = Math.max(2,
1060                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1061                             KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT,
1062                             DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT));
1063             final int highestTimeoutCount = Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT,
1064                     Math.max(EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT,
1065                             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT));
1066             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT = Math.max(highestTimeoutCount,
1067                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1068                             KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT,
1069                             DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT));
1070             EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS = DeviceConfig.getLong(
1071                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1072                     KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS,
1073                     DEFAULT_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS);
1074             EXECUTION_SAFEGUARDS_UDC_ANR_COUNT = Math.max(1,
1075                     DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1076                             KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT,
1077                             DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT));
1078             EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS = DeviceConfig.getLong(
1079                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1080                     KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS,
1081                     DEFAULT_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS);
1082         }
1083 
1084         private void updateRuntimeConstantsLocked() {
1085             DeviceConfig.Properties properties = DeviceConfig.getProperties(
1086                     DeviceConfig.NAMESPACE_JOB_SCHEDULER,
1087                     KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
1088                     KEY_RUNTIME_MIN_GUARANTEE_MS, KEY_RUNTIME_MIN_EJ_GUARANTEE_MS,
1089                     KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
1090                     KEY_RUNTIME_MIN_UI_GUARANTEE_MS,
1091                     KEY_RUNTIME_UI_LIMIT_MS,
1092                     KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
1093                     KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS,
1094                     KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS);
1095 
1096             // Make sure min runtime for regular jobs is at least 10 minutes.
1097             RUNTIME_MIN_GUARANTEE_MS = Math.max(10 * MINUTE_IN_MILLIS,
1098                     properties.getLong(
1099                             KEY_RUNTIME_MIN_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_GUARANTEE_MS));
1100             // Make sure min runtime for expedited jobs is at least one minute.
1101             RUNTIME_MIN_EJ_GUARANTEE_MS = Math.max(MINUTE_IN_MILLIS,
1102                     properties.getLong(
1103                             KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_EJ_GUARANTEE_MS));
1104             RUNTIME_FREE_QUOTA_MAX_LIMIT_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
1105                     properties.getLong(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
1106                             DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS));
1107             // Make sure min runtime is at least as long as regular jobs.
1108             RUNTIME_MIN_UI_GUARANTEE_MS = Math.max(RUNTIME_MIN_GUARANTEE_MS,
1109                     properties.getLong(
1110                             KEY_RUNTIME_MIN_UI_GUARANTEE_MS, DEFAULT_RUNTIME_MIN_UI_GUARANTEE_MS));
1111             // Max limit should be at least the min guarantee AND the free quota.
1112             RUNTIME_UI_LIMIT_MS = Math.max(RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
1113                     Math.max(RUNTIME_MIN_UI_GUARANTEE_MS,
1114                             properties.getLong(
1115                                     KEY_RUNTIME_UI_LIMIT_MS, DEFAULT_RUNTIME_UI_LIMIT_MS)));
1116             // The buffer factor should be at least 1 (so we don't decrease the time).
1117             RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR = Math.max(1,
1118                     properties.getFloat(
1119                             KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
1120                             DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR
1121                     ));
1122             // Make sure min runtime is at least as long as other user-initiated jobs.
1123             RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS = Math.max(
1124                     RUNTIME_MIN_UI_GUARANTEE_MS,
1125                     properties.getLong(
1126                             KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
1127                             DEFAULT_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS));
1128             // The cumulative runtime limit should be at least the max execution limit.
1129             RUNTIME_CUMULATIVE_UI_LIMIT_MS = Math.max(RUNTIME_UI_LIMIT_MS,
1130                     properties.getLong(
1131                             KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS,
1132                             DEFAULT_RUNTIME_CUMULATIVE_UI_LIMIT_MS));
1133 
1134             RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS = properties.getBoolean(
1135                     KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS,
1136                     DEFAULT_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS);
1137         }
1138 
1139         private boolean updateTareSettingsLocked(@EconomyManager.EnabledMode int enabledMode) {
1140             boolean changed = false;
1141             final boolean useTare = enabledMode == EconomyManager.ENABLED_MODE_ON;
1142             if (USE_TARE_POLICY != useTare) {
1143                 USE_TARE_POLICY = useTare;
1144                 changed = true;
1145             }
1146             return changed;
1147         }
1148 
1149         void dump(IndentingPrintWriter pw) {
1150             pw.println("Settings:");
1151             pw.increaseIndent();
1152             pw.print(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
1153                     MIN_READY_NON_ACTIVE_JOBS_COUNT).println();
1154             pw.print(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
1155                     MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS).println();
1156             pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
1157             pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
1158 
1159             pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
1160             pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
1161             pw.print(KEY_SYSTEM_STOP_TO_FAILURE_RATIO, SYSTEM_STOP_TO_FAILURE_RATIO).println();
1162             pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
1163             pw.print(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
1164             pw.print(KEY_CONN_USE_CELL_SIGNAL_STRENGTH, CONN_USE_CELL_SIGNAL_STRENGTH).println();
1165             pw.print(KEY_CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS, CONN_UPDATE_ALL_JOBS_MIN_INTERVAL_MS)
1166                     .println();
1167             pw.print(KEY_CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC, CONN_LOW_SIGNAL_STRENGTH_RELAX_FRAC)
1168                     .println();
1169             pw.print(KEY_PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS,
1170                     PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS).println();
1171 
1172             pw.print(KEY_ENABLE_API_QUOTAS, ENABLE_API_QUOTAS).println();
1173             pw.print(KEY_API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT).println();
1174             pw.print(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println();
1175             pw.print(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
1176                     API_QUOTA_SCHEDULE_THROW_EXCEPTION).println();
1177             pw.print(KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
1178                     API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT).println();
1179 
1180             pw.print(KEY_ENABLE_EXECUTION_SAFEGUARDS_UDC, ENABLE_EXECUTION_SAFEGUARDS_UDC)
1181                     .println();
1182             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT,
1183                     EXECUTION_SAFEGUARDS_UDC_TIMEOUT_UIJ_COUNT).println();
1184             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT,
1185                     EXECUTION_SAFEGUARDS_UDC_TIMEOUT_EJ_COUNT).println();
1186             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT,
1187                     EXECUTION_SAFEGUARDS_UDC_TIMEOUT_REG_COUNT).println();
1188             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT,
1189                     EXECUTION_SAFEGUARDS_UDC_TIMEOUT_TOTAL_COUNT).println();
1190             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS,
1191                     EXECUTION_SAFEGUARDS_UDC_TIMEOUT_WINDOW_MS).println();
1192             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_COUNT,
1193                     EXECUTION_SAFEGUARDS_UDC_ANR_COUNT).println();
1194             pw.print(KEY_EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS,
1195                     EXECUTION_SAFEGUARDS_UDC_ANR_WINDOW_MS).println();
1196 
1197             pw.print(KEY_RUNTIME_MIN_GUARANTEE_MS, RUNTIME_MIN_GUARANTEE_MS).println();
1198             pw.print(KEY_RUNTIME_MIN_EJ_GUARANTEE_MS, RUNTIME_MIN_EJ_GUARANTEE_MS).println();
1199             pw.print(KEY_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS, RUNTIME_FREE_QUOTA_MAX_LIMIT_MS)
1200                     .println();
1201             pw.print(KEY_RUNTIME_MIN_UI_GUARANTEE_MS, RUNTIME_MIN_UI_GUARANTEE_MS).println();
1202             pw.print(KEY_RUNTIME_UI_LIMIT_MS, RUNTIME_UI_LIMIT_MS).println();
1203             pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR,
1204                     RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR).println();
1205             pw.print(KEY_RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS,
1206                     RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS).println();
1207             pw.print(KEY_RUNTIME_CUMULATIVE_UI_LIMIT_MS, RUNTIME_CUMULATIVE_UI_LIMIT_MS).println();
1208             pw.print(KEY_RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS,
1209                     RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS).println();
1210 
1211             pw.print(KEY_PERSIST_IN_SPLIT_FILES, PERSIST_IN_SPLIT_FILES).println();
1212             pw.print(KEY_MAX_NUM_PERSISTED_JOB_WORK_ITEMS, MAX_NUM_PERSISTED_JOB_WORK_ITEMS)
1213                     .println();
1214 
1215             pw.print(Settings.Global.ENABLE_TARE, USE_TARE_POLICY).println();
1216 
1217             pw.decreaseIndent();
1218         }
1219 
1220         void dump(ProtoOutputStream proto) {
1221             proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT,
1222                     MIN_READY_NON_ACTIVE_JOBS_COUNT);
1223             proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
1224                     MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
1225             proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
1226             proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
1227 
1228             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS);
1229             proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS);
1230             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
1231             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
1232 
1233             proto.write(ConstantsProto.ENABLE_API_QUOTAS, ENABLE_API_QUOTAS);
1234             proto.write(ConstantsProto.API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT);
1235             proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS);
1236             proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION,
1237                     API_QUOTA_SCHEDULE_THROW_EXCEPTION);
1238             proto.write(ConstantsProto.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT,
1239                     API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT);
1240         }
1241     }
1242 
1243     final Constants mConstants;
1244     final ConstantsObserver mConstantsObserver;
1245 
1246     /**
1247      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
1248      * still clean up. On reinstall the package will have a new uid.
1249      */
1250     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1251         @Override
1252         public void onReceive(Context context, Intent intent) {
1253             final String action = intent.getAction();
1254             if (DEBUG) {
1255                 Slog.d(TAG, "Receieved: " + action);
1256             }
1257             final String pkgName = getPackageName(intent);
1258             final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1259 
1260             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
1261                 synchronized (mPermissionCache) {
1262                     // Something changed. Better clear the cached permission set.
1263                     mPermissionCache.remove(pkgUid);
1264                 }
1265                 // Purge the app's jobs if the whole package was just disabled.  When this is
1266                 // the case the component name will be a bare package name.
1267                 if (pkgName != null && pkgUid != -1) {
1268                     final String[] changedComponents = intent.getStringArrayExtra(
1269                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1270                     if (changedComponents != null) {
1271                         for (String component : changedComponents) {
1272                             if (component.equals(pkgName)) {
1273                                 if (DEBUG) {
1274                                     Slog.d(TAG, "Package state change: " + pkgName);
1275                                 }
1276                                 try {
1277                                     final int userId = UserHandle.getUserId(pkgUid);
1278                                     IPackageManager pm = AppGlobals.getPackageManager();
1279                                     final int state =
1280                                             pm.getApplicationEnabledSetting(pkgName, userId);
1281                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
1282                                             || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
1283                                         if (DEBUG) {
1284                                             Slog.d(TAG, "Removing jobs for package " + pkgName
1285                                                     + " in user " + userId);
1286                                         }
1287                                         synchronized (mLock) {
1288                                             // There's no guarantee that the process has been
1289                                             // stopped by the time we get here, but since this is
1290                                             // a user-initiated action, it should be fine to just
1291                                             // put USER instead of UNINSTALL or DISABLED.
1292                                             cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
1293                                                     /* includeSchedulingApp */ true,
1294                                                     /* includeSourceApp */ true,
1295                                                     JobParameters.STOP_REASON_USER,
1296                                                     JobParameters.INTERNAL_STOP_REASON_UNINSTALL,
1297                                                     "app disabled");
1298                                         }
1299                                     }
1300                                 } catch (RemoteException | IllegalArgumentException e) {
1301                                     /*
1302                                      * IllegalArgumentException means that the package doesn't exist.
1303                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
1304                                      * behind outright uninstall, so by the time we try to act it's gone.
1305                                      * We don't need to act on this PACKAGE_CHANGED when this happens;
1306                                      * we'll get a PACKAGE_REMOVED later and clean up then.
1307                                      *
1308                                      * RemoteException can't actually happen; the package manager is
1309                                      * running in this same process.
1310                                      */
1311                                 }
1312                                 break;
1313                             }
1314                         }
1315                         if (DEBUG) {
1316                             Slog.d(TAG, "Something in " + pkgName
1317                                     + " changed. Reevaluating controller states.");
1318                         }
1319                         synchronized (mLock) {
1320                             for (int c = mControllers.size() - 1; c >= 0; --c) {
1321                                 mControllers.get(c).reevaluateStateLocked(pkgUid);
1322                             }
1323                         }
1324                     }
1325                 } else {
1326                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
1327                 }
1328             } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1329                 synchronized (mPermissionCache) {
1330                     // Something changed. Better clear the cached permission set.
1331                     mPermissionCache.remove(pkgUid);
1332                 }
1333                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1334                     synchronized (mLock) {
1335                         mUidToPackageCache.remove(pkgUid);
1336                     }
1337                 }
1338             } else if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
1339                 synchronized (mPermissionCache) {
1340                     mPermissionCache.remove(pkgUid);
1341                 }
1342                 if (DEBUG) {
1343                     Slog.d(TAG, "Removing jobs for " + pkgName + " (uid=" + pkgUid + ")");
1344                 }
1345                 synchronized (mLock) {
1346                     mUidToPackageCache.remove(pkgUid);
1347                     // There's no guarantee that the process has been stopped by the time we
1348                     // get here, but since this is generally a user-initiated action, it should
1349                     // be fine to just put USER instead of UNINSTALL or DISABLED.
1350                     cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
1351                             /* includeSchedulingApp */ true, /* includeSourceApp */ true,
1352                             JobParameters.STOP_REASON_USER,
1353                             JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
1354                     for (int c = 0; c < mControllers.size(); ++c) {
1355                         mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
1356                     }
1357                     mDebuggableApps.remove(pkgName);
1358                     mConcurrencyManager.onAppRemovedLocked(pkgName, pkgUid);
1359                 }
1360             } else if (Intent.ACTION_UID_REMOVED.equals(action)) {
1361                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1362                     synchronized (mLock) {
1363                         mUidBiasOverride.delete(pkgUid);
1364                         mUidCapabilities.delete(pkgUid);
1365                         mUidProcStates.delete(pkgUid);
1366                     }
1367                 }
1368             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
1369                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
1370                 synchronized (mLock) {
1371                     for (int c = 0; c < mControllers.size(); ++c) {
1372                         mControllers.get(c).onUserAddedLocked(userId);
1373                     }
1374                 }
1375             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
1376                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
1377                 if (DEBUG) {
1378                     Slog.d(TAG, "Removing jobs for user: " + userId);
1379                 }
1380                 synchronized (mLock) {
1381                     mUidToPackageCache.clear();
1382                     cancelJobsForUserLocked(userId);
1383                     for (int c = 0; c < mControllers.size(); ++c) {
1384                         mControllers.get(c).onUserRemovedLocked(userId);
1385                     }
1386                 }
1387                 mConcurrencyManager.onUserRemoved(userId);
1388                 synchronized (mPermissionCache) {
1389                     for (int u = mPermissionCache.size() - 1; u >= 0; --u) {
1390                         final int uid = mPermissionCache.keyAt(u);
1391                         if (userId == UserHandle.getUserId(uid)) {
1392                             mPermissionCache.removeAt(u);
1393                         }
1394                     }
1395                 }
1396             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
1397                 // Has this package scheduled any jobs, such that we will take action
1398                 // if it were to be force-stopped?
1399                 if (pkgUid != -1) {
1400                     ArraySet<JobStatus> jobsForUid;
1401                     synchronized (mLock) {
1402                         jobsForUid = mJobs.getJobsByUid(pkgUid);
1403                     }
1404                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1405                         if (jobsForUid.valueAt(i).getSourcePackageName().equals(pkgName)) {
1406                             if (DEBUG) {
1407                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
1408                                         + pkgUid + " has jobs");
1409                             }
1410                             setResultCode(Activity.RESULT_OK);
1411                             break;
1412                         }
1413                     }
1414                 }
1415             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
1416                 // possible force-stop
1417                 if (pkgUid != -1) {
1418                     if (DEBUG) {
1419                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
1420                     }
1421                     synchronized (mLock) {
1422                         // Exclude jobs scheduled on behalf of this app for now because SyncManager
1423                         // and other job proxy agents may not know to reschedule the job properly
1424                         // after force stop.
1425                         // TODO(209852664): determine how to best handle syncs & other proxied jobs
1426                         cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
1427                                 /* includeSchedulingApp */ true, /* includeSourceApp */ false,
1428                                 JobParameters.STOP_REASON_USER,
1429                                 JobParameters.INTERNAL_STOP_REASON_CANCELED,
1430                                 "app force stopped");
1431                     }
1432                 }
1433             }
1434         }
1435     };
1436 
1437     private String getPackageName(Intent intent) {
1438         Uri uri = intent.getData();
1439         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
1440         return pkg;
1441     }
1442 
1443     final private IUidObserver mUidObserver = new UidObserver() {
1444         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
1445                 int capability) {
1446             final SomeArgs args = SomeArgs.obtain();
1447             args.argi1 = uid;
1448             args.argi2 = procState;
1449             args.argi3 = capability;
1450             mHandler.obtainMessage(MSG_UID_STATE_CHANGED, args).sendToTarget();
1451         }
1452 
1453         @Override public void onUidGone(int uid, boolean disabled) {
1454             mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
1455         }
1456 
1457         @Override public void onUidActive(int uid) {
1458             mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
1459         }
1460 
1461         @Override public void onUidIdle(int uid, boolean disabled) {
1462             mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
1463         }
1464     };
1465 
1466     public Context getTestableContext() {
1467         return getContext();
1468     }
1469 
1470     public Object getLock() {
1471         return mLock;
1472     }
1473 
1474     public JobStore getJobStore() {
1475         return mJobs;
1476     }
1477 
1478     public Constants getConstants() {
1479         return mConstants;
1480     }
1481 
1482     @NonNull
1483     PendingJobQueue getPendingJobQueue() {
1484         return mPendingJobQueue;
1485     }
1486 
1487     @NonNull
1488     public WorkSource deriveWorkSource(int sourceUid, @Nullable String sourcePackageName) {
1489         if (WorkSource.isChainedBatteryAttributionEnabled(getContext())) {
1490             WorkSource ws = new WorkSource();
1491             ws.createWorkChain()
1492                     .addNode(sourceUid, sourcePackageName)
1493                     .addNode(Process.SYSTEM_UID, "JobScheduler");
1494             return ws;
1495         } else {
1496             return sourcePackageName == null
1497                     ? new WorkSource(sourceUid) : new WorkSource(sourceUid, sourcePackageName);
1498         }
1499     }
1500 
1501     @Nullable
1502     @GuardedBy("mLock")
1503     public ArraySet<String> getPackagesForUidLocked(final int uid) {
1504         ArraySet<String> packages = mUidToPackageCache.get(uid);
1505         if (packages == null) {
1506             try {
1507                 String[] pkgs = AppGlobals.getPackageManager()
1508                         .getPackagesForUid(uid);
1509                 if (pkgs != null) {
1510                     for (String pkg : pkgs) {
1511                         mUidToPackageCache.add(uid, pkg);
1512                     }
1513                     packages = mUidToPackageCache.get(uid);
1514                 }
1515             } catch (RemoteException e) {
1516                 // Shouldn't happen.
1517             }
1518         }
1519         return packages;
1520     }
1521 
1522     @Override
1523     public void onUserStarting(@NonNull TargetUser user) {
1524         synchronized (mLock) {
1525             mStartedUsers = ArrayUtils.appendInt(mStartedUsers, user.getUserIdentifier());
1526         }
1527     }
1528 
1529     /** Start jobs after user is available, delayed by a few seconds since non-urgent. */
1530     @Override
1531     public void onUserCompletedEvent(@NonNull TargetUser user, UserCompletedEventType eventType) {
1532         if (eventType.includesOnUserStarting() || eventType.includesOnUserUnlocked()) {
1533             // onUserStarting: direct-boot-aware jobs can safely run
1534             // onUserUnlocked: direct-boot-UNaware jobs can safely run.
1535             mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1536         }
1537     }
1538 
1539     @Override
1540     public void onUserStopping(@NonNull TargetUser user) {
1541         synchronized (mLock) {
1542             mStartedUsers = ArrayUtils.removeInt(mStartedUsers, user.getUserIdentifier());
1543         }
1544     }
1545 
1546     /**
1547      * Return whether an UID is active or idle.
1548      */
1549     private boolean isUidActive(int uid) {
1550         return mAppStateTracker.isUidActiveSynced(uid);
1551     }
1552 
1553     private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
1554 
1555     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
1556             int userId, @Nullable String namespace, String tag) {
1557         // Rate limit excessive schedule() calls.
1558         final String servicePkg = job.getService().getPackageName();
1559         if (job.isPersisted() && (packageName == null || packageName.equals(servicePkg))) {
1560             // Only limit schedule calls for persisted jobs scheduled by the app itself.
1561             final String pkg = packageName == null ? servicePkg : packageName;
1562             if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
1563                 if (mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED)) {
1564                     // Don't log too frequently
1565                     Slog.wtf(TAG, userId + "-" + pkg + " has called schedule() too many times");
1566                     mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_LOGGED);
1567                 }
1568                 mAppStandbyInternal.restrictApp(
1569                         pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
1570                 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
1571                     final boolean isDebuggable;
1572                     synchronized (mLock) {
1573                         if (!mDebuggableApps.containsKey(packageName)) {
1574                             try {
1575                                 final ApplicationInfo appInfo = AppGlobals.getPackageManager()
1576                                         .getApplicationInfo(pkg, 0, userId);
1577                                 if (appInfo != null) {
1578                                     mDebuggableApps.put(packageName,
1579                                             (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
1580                                 } else {
1581                                     return JobScheduler.RESULT_FAILURE;
1582                                 }
1583                             } catch (RemoteException e) {
1584                                 throw new RuntimeException(e);
1585                             }
1586                         }
1587                         isDebuggable = mDebuggableApps.get(packageName);
1588                     }
1589                     if (isDebuggable) {
1590                         // Only throw the exception for debuggable apps.
1591                         throw new LimitExceededException(
1592                                 "schedule()/enqueue() called more than "
1593                                         + mQuotaTracker.getLimit(
1594                                         QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
1595                                         + " times in the past "
1596                                         + mQuotaTracker.getWindowSizeMs(
1597                                         QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED)
1598                                         + "ms. See the documentation for more information.");
1599                     }
1600                 }
1601                 if (mConstants.API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT) {
1602                     return JobScheduler.RESULT_FAILURE;
1603                 }
1604             }
1605             mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
1606         }
1607 
1608         if (mActivityManagerInternal.isAppStartModeDisabled(uId, servicePkg)) {
1609             Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1610                     + " -- package not allowed to start");
1611             Counter.logIncrementWithUid(
1612                     "job_scheduler.value_cntr_w_uid_schedule_failure_app_start_mode_disabled",
1613                     uId);
1614             return JobScheduler.RESULT_FAILURE;
1615         }
1616 
1617         if (job.getRequiredNetwork() != null) {
1618             sInitialJobEstimatedNetworkDownloadKBLogger.logSample(
1619                     safelyScaleBytesToKBForHistogram(
1620                             job.getEstimatedNetworkDownloadBytes()));
1621             sInitialJobEstimatedNetworkUploadKBLogger.logSample(
1622                     safelyScaleBytesToKBForHistogram(job.getEstimatedNetworkUploadBytes()));
1623             sJobMinimumChunkKBLogger.logSampleWithUid(uId,
1624                     safelyScaleBytesToKBForHistogram(job.getMinimumNetworkChunkBytes()));
1625             if (work != null) {
1626                 sInitialJwiEstimatedNetworkDownloadKBLogger.logSample(
1627                         safelyScaleBytesToKBForHistogram(
1628                                 work.getEstimatedNetworkDownloadBytes()));
1629                 sInitialJwiEstimatedNetworkUploadKBLogger.logSample(
1630                         safelyScaleBytesToKBForHistogram(
1631                                 work.getEstimatedNetworkUploadBytes()));
1632                 sJwiMinimumChunkKBLogger.logSampleWithUid(uId,
1633                         safelyScaleBytesToKBForHistogram(
1634                                 work.getMinimumNetworkChunkBytes()));
1635             }
1636         }
1637 
1638         if (work != null) {
1639             Counter.logIncrementWithUid(
1640                     "job_scheduler.value_cntr_w_uid_job_work_items_enqueued", uId);
1641         }
1642 
1643         synchronized (mLock) {
1644             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, namespace, job.getId());
1645 
1646             if (work != null && toCancel != null) {
1647                 // Fast path: we are adding work to an existing job, and the JobInfo is not
1648                 // changing.  We can just directly enqueue this work in to the job.
1649                 if (toCancel.getJob().equals(job)) {
1650                     // On T and below, JobWorkItem count was unlimited but they could not be
1651                     // persisted. Now in U and above, we allow persisting them. In both cases,
1652                     // there is a danger of apps adding too many JobWorkItems and causing the
1653                     // system to OOM since we keep everything in memory. The persisting danger
1654                     // is greater because it could technically lead to a boot loop if the system
1655                     // keeps trying to load all the JobWorkItems that led to the initial OOM.
1656                     // Therefore, for now (partly for app compatibility), we tackle the latter
1657                     // and limit the number of JobWorkItems that can be persisted.
1658                     // Moving forward, we should look into two things:
1659                     //   1. Limiting the number of unpersisted JobWorkItems
1660                     //   2. Offloading some state to disk so we don't keep everything in memory
1661                     // TODO(273758274): improve JobScheduler's resilience and memory management
1662                     if (toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
1663                             && toCancel.isPersisted()) {
1664                         Slog.w(TAG, "Too many JWIs for uid " + uId);
1665                         throw new IllegalStateException("Apps may not persist more than "
1666                                 + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
1667                                 + " JobWorkItems per job");
1668                     }
1669 
1670                     toCancel.enqueueWorkLocked(work);
1671                     if (toCancel.getJob().isUserInitiated()) {
1672                         // The app is in a state to successfully schedule a UI job. Presumably, the
1673                         // user has asked for this additional bit of work, so remove any demotion
1674                         // flags. Only do this for UI jobs since they have strict scheduling
1675                         // requirements; it's harder to assume other jobs were scheduled due to
1676                         // user interaction/request.
1677                         toCancel.removeInternalFlags(
1678                                 JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER
1679                                         | JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ);
1680                     }
1681                     mJobs.touchJob(toCancel);
1682                     sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, toCancel.getWorkCount());
1683 
1684                     // If any of work item is enqueued when the source is in the foreground,
1685                     // exempt the entire job.
1686                     toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
1687 
1688                     return JobScheduler.RESULT_SUCCESS;
1689                 }
1690             }
1691 
1692             JobStatus jobStatus =
1693                     JobStatus.createFromJobInfo(job, uId, packageName, userId, namespace, tag);
1694 
1695             // Return failure early if expedited job quota used up.
1696             if (jobStatus.isRequestedExpeditedJob()) {
1697                 if ((mConstants.USE_TARE_POLICY && !mTareController.canScheduleEJ(jobStatus))
1698                         || (!mConstants.USE_TARE_POLICY
1699                         && !mQuotaController.isWithinEJQuotaLocked(jobStatus))) {
1700                     Counter.logIncrementWithUid(
1701                             "job_scheduler.value_cntr_w_uid_schedule_failure_ej_out_of_quota",
1702                             uId);
1703                     return JobScheduler.RESULT_FAILURE;
1704                 }
1705             }
1706 
1707             // Give exemption if the source is in the foreground just now.
1708             // Note if it's a sync job, this method is called on the handler so it's not exactly
1709             // the state when requestSync() was called, but that should be fine because of the
1710             // 1 minute foreground grace period.
1711             jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
1712 
1713             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
1714             // Jobs on behalf of others don't apply to the per-app job cap
1715             if (packageName == null) {
1716                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1717                     Slog.w(TAG, "Too many jobs for uid " + uId);
1718                     Counter.logIncrementWithUid(
1719                             "job_scheduler.value_cntr_w_uid_max_scheduling_limit_hit", uId);
1720                     throw new IllegalStateException("Apps may not schedule more than "
1721                             + MAX_JOBS_PER_APP + " distinct jobs");
1722                 }
1723             }
1724 
1725             // This may throw a SecurityException.
1726             jobStatus.prepareLocked();
1727 
1728             if (toCancel != null) {
1729                 // On T and below, JobWorkItem count was unlimited but they could not be
1730                 // persisted. Now in U and above, we allow persisting them. In both cases,
1731                 // there is a danger of apps adding too many JobWorkItems and causing the
1732                 // system to OOM since we keep everything in memory. The persisting danger
1733                 // is greater because it could technically lead to a boot loop if the system
1734                 // keeps trying to load all the JobWorkItems that led to the initial OOM.
1735                 // Therefore, for now (partly for app compatibility), we tackle the latter
1736                 // and limit the number of JobWorkItems that can be persisted.
1737                 // Moving forward, we should look into two things:
1738                 //   1. Limiting the number of unpersisted JobWorkItems
1739                 //   2. Offloading some state to disk so we don't keep everything in memory
1740                 // TODO(273758274): improve JobScheduler's resilience and memory management
1741                 if (work != null && toCancel.isPersisted()
1742                         && toCancel.getWorkCount() >= mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS) {
1743                     Slog.w(TAG, "Too many JWIs for uid " + uId);
1744                     throw new IllegalStateException("Apps may not persist more than "
1745                             + mConstants.MAX_NUM_PERSISTED_JOB_WORK_ITEMS
1746                             + " JobWorkItems per job");
1747                 }
1748 
1749                 // Implicitly replaces the existing job record with the new instance
1750                 cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
1751                         JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app");
1752             } else {
1753                 startTrackingJobLocked(jobStatus, null);
1754             }
1755 
1756             if (work != null) {
1757                 // If work has been supplied, enqueue it into the new job.
1758                 jobStatus.enqueueWorkLocked(work);
1759                 sEnqueuedJwiHighWaterMarkLogger.logSampleWithUid(uId, jobStatus.getWorkCount());
1760             }
1761 
1762             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
1763                     uId, null, jobStatus.getBatteryName(),
1764                     FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1765                     JobProtoEnums.INTERNAL_STOP_REASON_UNKNOWN, jobStatus.getStandbyBucket(),
1766                     jobStatus.getLoggingJobId(),
1767                     jobStatus.hasChargingConstraint(),
1768                     jobStatus.hasBatteryNotLowConstraint(),
1769                     jobStatus.hasStorageNotLowConstraint(),
1770                     jobStatus.hasTimingDelayConstraint(),
1771                     jobStatus.hasDeadlineConstraint(),
1772                     jobStatus.hasIdleConstraint(),
1773                     jobStatus.hasConnectivityConstraint(),
1774                     jobStatus.hasContentTriggerConstraint(),
1775                     jobStatus.isRequestedExpeditedJob(),
1776                     /* isRunningAsExpeditedJob */ false,
1777                     JobProtoEnums.STOP_REASON_UNDEFINED,
1778                     jobStatus.getJob().isPrefetch(),
1779                     jobStatus.getJob().getPriority(),
1780                     jobStatus.getEffectivePriority(),
1781                     jobStatus.getNumPreviousAttempts(),
1782                     jobStatus.getJob().getMaxExecutionDelayMillis(),
1783                     /* isDeadlineConstraintSatisfied */ false,
1784                     /* isChargingSatisfied */ false,
1785                     /* batteryNotLowSatisfied */ false,
1786                     /* storageNotLowSatisfied */false,
1787                     /* timingDelayConstraintSatisfied */ false,
1788                     /* isDeviceIdleSatisfied */ false,
1789                     /* hasConnectivityConstraintSatisfied */ false,
1790                     /* hasContentTriggerConstraintSatisfied */ false,
1791                     /* jobStartLatencyMs */ 0,
1792                     jobStatus.getJob().isUserInitiated(),
1793                     /* isRunningAsUserInitiatedJob */ false,
1794                     jobStatus.getJob().isPeriodic(),
1795                     jobStatus.getJob().getMinLatencyMillis(),
1796                     jobStatus.getEstimatedNetworkDownloadBytes(),
1797                     jobStatus.getEstimatedNetworkUploadBytes(),
1798                     jobStatus.getWorkCount(),
1799                     ActivityManager.processStateAmToProto(mUidProcStates.get(jobStatus.getUid())),
1800                     jobStatus.getNamespaceHash());
1801 
1802             // If the job is immediately ready to run, then we can just immediately
1803             // put it in the pending list and try to schedule it.  This is especially
1804             // important for jobs with a 0 deadline constraint, since they will happen a fair
1805             // amount, we want to handle them as quickly as possible, and semantically we want to
1806             // make sure we have started holding the wake lock for the job before returning to
1807             // the caller.
1808             // If the job is not yet ready to run, there is nothing more to do -- we are
1809             // now just waiting for one of its controllers to change state and schedule
1810             // the job appropriately.
1811             if (isReadyToBeExecutedLocked(jobStatus)) {
1812                 // This is a new job, we can just immediately put it on the pending
1813                 // list and try to run it.
1814                 mJobPackageTracker.notePending(jobStatus);
1815                 mPendingJobQueue.add(jobStatus);
1816                 maybeRunPendingJobsLocked();
1817             }
1818         }
1819         return JobScheduler.RESULT_SUCCESS;
1820     }
1821 
1822     private ArrayMap<String, List<JobInfo>> getPendingJobs(int uid) {
1823         final ArrayMap<String, List<JobInfo>> outMap = new ArrayMap<>();
1824         synchronized (mLock) {
1825             ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
1826             // Write out for loop to avoid creating an Iterator.
1827             for (int i = jobs.size() - 1; i >= 0; i--) {
1828                 final JobStatus job = jobs.valueAt(i);
1829                 List<JobInfo> outList = outMap.get(job.getNamespace());
1830                 if (outList == null) {
1831                     outList = new ArrayList<>();
1832                     outMap.put(job.getNamespace(), outList);
1833                 }
1834 
1835                 outList.add(job.getJob());
1836             }
1837             return outMap;
1838         }
1839     }
1840 
1841     private List<JobInfo> getPendingJobsInNamespace(int uid, @Nullable String namespace) {
1842         synchronized (mLock) {
1843             ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
1844             ArrayList<JobInfo> outList = new ArrayList<>();
1845             // Write out for loop to avoid addAll() creating an Iterator.
1846             for (int i = jobs.size() - 1; i >= 0; i--) {
1847                 final JobStatus job = jobs.valueAt(i);
1848                 if (Objects.equals(namespace, job.getNamespace())) {
1849                     outList.add(job.getJob());
1850                 }
1851             }
1852             return outList;
1853         }
1854     }
1855 
1856     @JobScheduler.PendingJobReason
1857     private int getPendingJobReason(int uid, String namespace, int jobId) {
1858         int reason;
1859         // Some apps may attempt to query this frequently, so cache the reason under a separate lock
1860         // so that the rest of JS processing isn't negatively impacted.
1861         synchronized (mPendingJobReasonCache) {
1862             SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
1863             if (jobIdToReason != null) {
1864                 reason = jobIdToReason.get(jobId, JobScheduler.PENDING_JOB_REASON_UNDEFINED);
1865                 if (reason != JobScheduler.PENDING_JOB_REASON_UNDEFINED) {
1866                     return reason;
1867                 }
1868             }
1869         }
1870         synchronized (mLock) {
1871             reason = getPendingJobReasonLocked(uid, namespace, jobId);
1872             if (DEBUG) {
1873                 Slog.v(TAG, "getPendingJobReason("
1874                         + uid + "," + namespace + "," + jobId + ")=" + reason);
1875             }
1876         }
1877         synchronized (mPendingJobReasonCache) {
1878             SparseIntArray jobIdToReason = mPendingJobReasonCache.get(uid, namespace);
1879             if (jobIdToReason == null) {
1880                 jobIdToReason = new SparseIntArray();
1881                 mPendingJobReasonCache.add(uid, namespace, jobIdToReason);
1882             }
1883             jobIdToReason.put(jobId, reason);
1884         }
1885         return reason;
1886     }
1887 
1888     @VisibleForTesting
1889     @JobScheduler.PendingJobReason
1890     int getPendingJobReason(JobStatus job) {
1891         return getPendingJobReason(job.getUid(), job.getNamespace(), job.getJobId());
1892     }
1893 
1894     @JobScheduler.PendingJobReason
1895     @GuardedBy("mLock")
1896     private int getPendingJobReasonLocked(int uid, String namespace, int jobId) {
1897         // Very similar code to isReadyToBeExecutedLocked.
1898 
1899         JobStatus job = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
1900         if (job == null) {
1901             // Job doesn't exist.
1902             return JobScheduler.PENDING_JOB_REASON_INVALID_JOB_ID;
1903         }
1904 
1905         if (isCurrentlyRunningLocked(job)) {
1906             return JobScheduler.PENDING_JOB_REASON_EXECUTING;
1907         }
1908 
1909         final boolean jobReady = job.isReady();
1910 
1911         if (DEBUG) {
1912             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1913                     + " ready=" + jobReady);
1914         }
1915 
1916         if (!jobReady) {
1917             return job.getPendingJobReason();
1918         }
1919 
1920         final boolean userStarted = areUsersStartedLocked(job);
1921 
1922         if (DEBUG) {
1923             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1924                     + " userStarted=" + userStarted);
1925         }
1926         if (!userStarted) {
1927             return JobScheduler.PENDING_JOB_REASON_USER;
1928         }
1929 
1930         final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
1931         if (DEBUG) {
1932             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1933                     + " backingUp=" + backingUp);
1934         }
1935 
1936         if (backingUp) {
1937             // TODO: Should we make a special reason for this?
1938             return JobScheduler.PENDING_JOB_REASON_APP;
1939         }
1940 
1941         JobRestriction restriction = checkIfRestricted(job);
1942         if (DEBUG) {
1943             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1944                     + " restriction=" + restriction);
1945         }
1946         if (restriction != null) {
1947             return restriction.getPendingReason();
1948         }
1949 
1950         // The following can be a little more expensive (especially jobActive, since we need to
1951         // go through the array of all potentially active jobs), so we are doing them
1952         // later...  but still before checking with the package manager!
1953         final boolean jobPending = mPendingJobQueue.contains(job);
1954 
1955 
1956         if (DEBUG) {
1957             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1958                     + " pending=" + jobPending);
1959         }
1960 
1961         if (jobPending) {
1962             // We haven't started the job for some reason. Presumably, there are too many jobs
1963             // running.
1964             return JobScheduler.PENDING_JOB_REASON_DEVICE_STATE;
1965         }
1966 
1967         final boolean jobActive = mConcurrencyManager.isJobRunningLocked(job);
1968 
1969         if (DEBUG) {
1970             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1971                     + " active=" + jobActive);
1972         }
1973         if (jobActive) {
1974             return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
1975         }
1976 
1977         // Validate that the defined package+service is still present & viable.
1978         final boolean componentUsable = isComponentUsable(job);
1979 
1980         if (DEBUG) {
1981             Slog.v(TAG, "getPendingJobReasonLocked: " + job.toShortString()
1982                     + " componentUsable=" + componentUsable);
1983         }
1984         if (!componentUsable) {
1985             return JobScheduler.PENDING_JOB_REASON_APP;
1986         }
1987 
1988         return JobScheduler.PENDING_JOB_REASON_UNDEFINED;
1989     }
1990 
1991     private JobInfo getPendingJob(int uid, @Nullable String namespace, int jobId) {
1992         synchronized (mLock) {
1993             ArraySet<JobStatus> jobs = mJobs.getJobsByUid(uid);
1994             for (int i = jobs.size() - 1; i >= 0; i--) {
1995                 JobStatus job = jobs.valueAt(i);
1996                 if (job.getJobId() == jobId && Objects.equals(namespace, job.getNamespace())) {
1997                     return job.getJob();
1998                 }
1999             }
2000             return null;
2001         }
2002     }
2003 
2004     @VisibleForTesting
2005     void notePendingUserRequestedAppStopInternal(@NonNull String packageName, int userId,
2006             @Nullable String debugReason) {
2007         final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId);
2008         if (packageUid < 0) {
2009             Slog.wtf(TAG, "Asked to stop jobs of an unknown package");
2010             return;
2011         }
2012         synchronized (mLock) {
2013             mConcurrencyManager.markJobsForUserStopLocked(userId, packageName, debugReason);
2014             final ArraySet<JobStatus> jobs = mJobs.getJobsByUid(packageUid);
2015             for (int i = jobs.size() - 1; i >= 0; i--) {
2016                 final JobStatus job = jobs.valueAt(i);
2017 
2018                 // For now, demote all jobs of the app. However, if the app was only doing work
2019                 // on behalf of another app and the user wanted just that work to stop, this
2020                 // unfairly penalizes any other jobs that may be scheduled.
2021                 // For example, if apps A & B ask app C to do something (thus A & B are "source"
2022                 // and C is "calling"), but only A's work was under way and the user wanted
2023                 // to stop only that work, B's jobs would be demoted as well.
2024                 // TODO(255768978): make it possible to demote only the relevant subset of jobs
2025                 job.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
2026 
2027                 // The app process will be killed soon. There's no point keeping its jobs in
2028                 // the pending queue to try and start them.
2029                 if (mPendingJobQueue.remove(job)) {
2030                     synchronized (mPendingJobReasonCache) {
2031                         SparseIntArray jobIdToReason = mPendingJobReasonCache.get(
2032                                 job.getUid(), job.getNamespace());
2033                         if (jobIdToReason == null) {
2034                             jobIdToReason = new SparseIntArray();
2035                             mPendingJobReasonCache.add(job.getUid(), job.getNamespace(),
2036                                     jobIdToReason);
2037                         }
2038                         jobIdToReason.put(job.getJobId(), JobScheduler.PENDING_JOB_REASON_USER);
2039                     }
2040                 }
2041             }
2042         }
2043     }
2044 
2045     private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
2046         // There's no guarantee that the process has been stopped by the time we get
2047         // here, but since this is a user-initiated action, it should be fine to just
2048         // put USER instead of UNINSTALL or DISABLED.
2049         cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
2050                 JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
2051     };
2052 
2053     private void cancelJobsForUserLocked(int userHandle) {
2054         mJobs.forEachJob(
2055                 (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle,
2056                 mCancelJobDueToUserRemovalConsumer);
2057     }
2058 
2059     private void cancelJobsForNonExistentUsers() {
2060         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
2061         synchronized (mLock) {
2062             mJobs.removeJobsOfUnlistedUsers(umi.getUserIds());
2063         }
2064         synchronized (mPendingJobReasonCache) {
2065             mPendingJobReasonCache.clear();
2066         }
2067     }
2068 
2069     private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
2070             boolean includeSchedulingApp, boolean includeSourceApp,
2071             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
2072         if (!includeSchedulingApp && !includeSourceApp) {
2073             Slog.wtfStack(TAG,
2074                     "Didn't indicate whether to cancel jobs for scheduling and/or source app");
2075             includeSourceApp = true;
2076         }
2077         if ("android".equals(pkgName)) {
2078             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
2079             return;
2080         }
2081         final ArraySet<JobStatus> jobsForUid = new ArraySet<>();
2082         if (includeSchedulingApp) {
2083             mJobs.getJobsByUid(uid, jobsForUid);
2084         }
2085         if (includeSourceApp) {
2086             mJobs.getJobsBySourceUid(uid, jobsForUid);
2087         }
2088         for (int i = jobsForUid.size() - 1; i >= 0; i--) {
2089             final JobStatus job = jobsForUid.valueAt(i);
2090             final boolean shouldCancel =
2091                     (includeSchedulingApp
2092                             && job.getServiceComponent().getPackageName().equals(pkgName))
2093                     || (includeSourceApp && job.getSourcePackageName().equals(pkgName));
2094             if (shouldCancel) {
2095                 cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
2096             }
2097         }
2098     }
2099 
2100     /**
2101      * Entry point from client to cancel all jobs scheduled for or from their uid.
2102      * This will remove the job from the master list, and cancel the job if it was staged for
2103      * execution or being executed.
2104      *
2105      * @param uid              Uid to check against for removal of a job.
2106      * @param includeSourceApp Whether to include jobs scheduled for this UID by another UID.
2107      *                         If false, only jobs scheduled by this UID will be cancelled.
2108      */
2109     public boolean cancelJobsForUid(int uid, boolean includeSourceApp,
2110             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
2111         return cancelJobsForUid(uid, includeSourceApp,
2112                 /* namespaceOnly */ false, /* namespace */ null,
2113                 reason, internalReasonCode, debugReason);
2114     }
2115 
2116     private boolean cancelJobsForUid(int uid, boolean includeSourceApp,
2117             boolean namespaceOnly, @Nullable String namespace,
2118             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
2119         // Non-null system namespace means the cancelling is limited to the namespace
2120         // and won't cause issues for the system at large.
2121         if (uid == Process.SYSTEM_UID && (!namespaceOnly || namespace == null)) {
2122             Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
2123             return false;
2124         }
2125 
2126         boolean jobsCanceled = false;
2127         synchronized (mLock) {
2128             final ArraySet<JobStatus> jobsForUid = new ArraySet<>();
2129             // Get jobs scheduled by the app.
2130             mJobs.getJobsByUid(uid, jobsForUid);
2131             if (includeSourceApp) {
2132                 // Get jobs scheduled for the app by someone else.
2133                 mJobs.getJobsBySourceUid(uid, jobsForUid);
2134             }
2135             for (int i = 0; i < jobsForUid.size(); i++) {
2136                 JobStatus toRemove = jobsForUid.valueAt(i);
2137                 if (!namespaceOnly || Objects.equals(namespace, toRemove.getNamespace())) {
2138                     cancelJobImplLocked(toRemove, null, reason, internalReasonCode, debugReason);
2139                     jobsCanceled = true;
2140                 }
2141             }
2142         }
2143         return jobsCanceled;
2144     }
2145 
2146     /**
2147      * Entry point from client to cancel the job corresponding to the jobId provided.
2148      * This will remove the job from the master list, and cancel the job if it was staged for
2149      * execution or being executed.
2150      *
2151      * @param uid   Uid of the calling client.
2152      * @param jobId Id of the job, provided at schedule-time.
2153      */
2154     private boolean cancelJob(int uid, String namespace, int jobId, int callingUid,
2155             @JobParameters.StopReason int reason) {
2156         JobStatus toCancel;
2157         synchronized (mLock) {
2158             toCancel = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
2159             if (toCancel != null) {
2160                 cancelJobImplLocked(toCancel, null, reason,
2161                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
2162                         "cancel() called by app, callingUid=" + callingUid
2163                                 + " uid=" + uid + " jobId=" + jobId);
2164             }
2165             return (toCancel != null);
2166         }
2167     }
2168 
2169     /**
2170      * Cancel the given job, stopping it if it's currently executing.  If {@code incomingJob}
2171      * is null, the cancelled job is removed outright from the system.  If
2172      * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
2173      * currently scheduled jobs.
2174      */
2175     @GuardedBy("mLock")
2176     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
2177             @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
2178         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
2179         cancelled.unprepareLocked();
2180         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
2181         // Remove from pending queue.
2182         if (mPendingJobQueue.remove(cancelled)) {
2183             mJobPackageTracker.noteNonpending(cancelled);
2184         }
2185         mChangedJobList.remove(cancelled);
2186         // Cancel if running.
2187         final boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked(
2188                 cancelled, reason, internalReasonCode, debugReason);
2189         // If the job was running, the JobServiceContext should log with state FINISHED.
2190         if (!wasRunning) {
2191             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
2192                     cancelled.getSourceUid(), null, cancelled.getBatteryName(),
2193                     FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED,
2194                     internalReasonCode, cancelled.getStandbyBucket(),
2195                     cancelled.getLoggingJobId(),
2196                     cancelled.hasChargingConstraint(),
2197                     cancelled.hasBatteryNotLowConstraint(),
2198                     cancelled.hasStorageNotLowConstraint(),
2199                     cancelled.hasTimingDelayConstraint(),
2200                     cancelled.hasDeadlineConstraint(),
2201                     cancelled.hasIdleConstraint(),
2202                     cancelled.hasConnectivityConstraint(),
2203                     cancelled.hasContentTriggerConstraint(),
2204                     cancelled.isRequestedExpeditedJob(),
2205                     /* isRunningAsExpeditedJob */ false,
2206                     reason,
2207                     cancelled.getJob().isPrefetch(),
2208                     cancelled.getJob().getPriority(),
2209                     cancelled.getEffectivePriority(),
2210                     cancelled.getNumPreviousAttempts(),
2211                     cancelled.getJob().getMaxExecutionDelayMillis(),
2212                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_DEADLINE),
2213                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_CHARGING),
2214                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_BATTERY_NOT_LOW),
2215                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_STORAGE_NOT_LOW),
2216                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_TIMING_DELAY),
2217                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
2218                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
2219                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
2220                     /* jobStartLatencyMs */ 0,
2221                     cancelled.getJob().isUserInitiated(),
2222                     /* isRunningAsUserInitiatedJob */ false,
2223                     cancelled.getJob().isPeriodic(),
2224                     cancelled.getJob().getMinLatencyMillis(),
2225                     cancelled.getEstimatedNetworkDownloadBytes(),
2226                     cancelled.getEstimatedNetworkUploadBytes(),
2227                     cancelled.getWorkCount(),
2228                     ActivityManager.processStateAmToProto(mUidProcStates.get(cancelled.getUid())),
2229                     cancelled.getNamespaceHash());
2230         }
2231         // If this is a replacement, bring in the new version of the job
2232         if (incomingJob != null) {
2233             if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
2234             startTrackingJobLocked(incomingJob, cancelled);
2235         }
2236         reportActiveLocked();
2237         if (mLastCancelledJobs.length > 0
2238                 && internalReasonCode == JobParameters.INTERNAL_STOP_REASON_CANCELED) {
2239             mLastCancelledJobs[mLastCancelledJobIndex] = cancelled;
2240             mLastCancelledJobTimeElapsed[mLastCancelledJobIndex] = sElapsedRealtimeClock.millis();
2241             mLastCancelledJobIndex = (mLastCancelledJobIndex + 1) % mLastCancelledJobs.length;
2242         }
2243     }
2244 
2245     void updateUidState(int uid, int procState, int capabilities) {
2246         if (DEBUG) {
2247             Slog.d(TAG, "UID " + uid + " proc state changed to "
2248                     + ActivityManager.procStateToString(procState)
2249                     + " with capabilities=" + ActivityManager.getCapabilitiesSummary(capabilities));
2250         }
2251         synchronized (mLock) {
2252             mUidProcStates.put(uid, procState);
2253             final int prevBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
2254             if (procState == ActivityManager.PROCESS_STATE_TOP) {
2255                 // Only use this if we are exactly the top app.  All others can live
2256                 // with just the foreground bias.  This means that persistent processes
2257                 // can never have the top app bias...  that is fine.
2258                 mUidBiasOverride.put(uid, JobInfo.BIAS_TOP_APP);
2259             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
2260                 mUidBiasOverride.put(uid, JobInfo.BIAS_FOREGROUND_SERVICE);
2261             } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
2262                 mUidBiasOverride.put(uid, JobInfo.BIAS_BOUND_FOREGROUND_SERVICE);
2263             } else {
2264                 mUidBiasOverride.delete(uid);
2265             }
2266             if (capabilities == ActivityManager.PROCESS_CAPABILITY_NONE
2267                     || procState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
2268                 mUidCapabilities.delete(uid);
2269             } else {
2270                 mUidCapabilities.put(uid, capabilities);
2271             }
2272             final int newBias = mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
2273             if (prevBias != newBias) {
2274                 if (DEBUG) {
2275                     Slog.d(TAG, "UID " + uid + " bias changed from " + prevBias + " to " + newBias);
2276                 }
2277                 for (int c = 0; c < mControllers.size(); ++c) {
2278                     mControllers.get(c).onUidBiasChangedLocked(uid, prevBias, newBias);
2279                 }
2280                 mConcurrencyManager.onUidBiasChangedLocked(prevBias, newBias);
2281             }
2282         }
2283     }
2284 
2285     /** Return the current bias of the given UID. */
2286     public int getUidBias(int uid) {
2287         synchronized (mLock) {
2288             return mUidBiasOverride.get(uid, JobInfo.BIAS_DEFAULT);
2289         }
2290     }
2291 
2292     /**
2293      * Return the current {@link ActivityManager#PROCESS_CAPABILITY_ALL capabilities}
2294      * of the given UID.
2295      */
2296     public int getUidCapabilities(int uid) {
2297         synchronized (mLock) {
2298             return mUidCapabilities.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE);
2299         }
2300     }
2301 
2302     /** Return the current proc state of the given UID. */
2303     public int getUidProcState(int uid) {
2304         synchronized (mLock) {
2305             return mUidProcStates.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN);
2306         }
2307     }
2308 
2309     @Override
2310     public void onDeviceIdleStateChanged(boolean deviceIdle) {
2311         synchronized (mLock) {
2312             if (DEBUG) {
2313                 Slog.d(TAG, "Doze state changed: " + deviceIdle);
2314             }
2315             if (!deviceIdle) {
2316                 // When coming out of idle, allow thing to start back up.
2317                 if (mReadyToRock) {
2318                     if (mLocalDeviceIdleController != null) {
2319                         if (!mReportedActive) {
2320                             mReportedActive = true;
2321                             mLocalDeviceIdleController.setJobsActive(true);
2322                         }
2323                     }
2324                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2325                 }
2326             }
2327         }
2328     }
2329 
2330     @Override
2331     public void onNetworkChanged(JobStatus jobStatus, Network newNetwork) {
2332         synchronized (mLock) {
2333             final JobServiceContext jsc =
2334                     mConcurrencyManager.getRunningJobServiceContextLocked(jobStatus);
2335             if (jsc != null) {
2336                 jsc.informOfNetworkChangeLocked(newNetwork);
2337             }
2338         }
2339     }
2340 
2341     @Override
2342     public void onRestrictedBucketChanged(List<JobStatus> jobs) {
2343         final int len = jobs.size();
2344         if (len == 0) {
2345             Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
2346             return;
2347         }
2348         synchronized (mLock) {
2349             for (int i = 0; i < len; ++i) {
2350                 JobStatus js = jobs.get(i);
2351                 for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
2352                     // Effective standby bucket can change after this in some situations so use
2353                     // the real bucket so that the job is tracked by the controllers.
2354                     if (js.getStandbyBucket() == RESTRICTED_INDEX) {
2355                         mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
2356                     } else {
2357                         mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
2358                     }
2359                 }
2360             }
2361         }
2362         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2363     }
2364 
2365     void reportActiveLocked() {
2366         // active is true if pending queue contains jobs OR some job is running.
2367         boolean active = mPendingJobQueue.size() > 0;
2368         if (!active) {
2369             final ArraySet<JobStatus> runningJobs = mConcurrencyManager.getRunningJobsLocked();
2370             for (int i = runningJobs.size() - 1; i >= 0; --i) {
2371                 final JobStatus job = runningJobs.valueAt(i);
2372                 if (!job.canRunInDoze()) {
2373                     // We will report active if we have a job running and it does not have an
2374                     // exception that allows it to run in Doze.
2375                     active = true;
2376                     break;
2377                 }
2378             }
2379         }
2380 
2381         if (mReportedActive != active) {
2382             mReportedActive = active;
2383             if (mLocalDeviceIdleController != null) {
2384                 mLocalDeviceIdleController.setJobsActive(active);
2385             }
2386         }
2387     }
2388 
2389     void reportAppUsage(String packageName, int userId) {
2390         // This app just transitioned into interactive use or near equivalent, so we should
2391         // take a look at its job state for feedback purposes.
2392     }
2393 
2394     /**
2395      * Initializes the system service.
2396      * <p>
2397      * Subclasses must define a single argument constructor that accepts the context
2398      * and passes it to super.
2399      * </p>
2400      *
2401      * @param context The system server context.
2402      */
2403     public JobSchedulerService(Context context) {
2404         super(context);
2405 
2406         mLocalPM = LocalServices.getService(PackageManagerInternal.class);
2407         mActivityManagerInternal = Objects.requireNonNull(
2408                 LocalServices.getService(ActivityManagerInternal.class));
2409 
2410         mHandler = new JobHandler(AppSchedulingModuleThread.get().getLooper());
2411         mConstants = new Constants();
2412         mConstantsObserver = new ConstantsObserver();
2413         mJobSchedulerStub = new JobSchedulerStub();
2414 
2415         mConcurrencyManager = new JobConcurrencyManager(this);
2416 
2417         // Set up the app standby bucketing tracker
2418         mStandbyTracker = new StandbyTracker();
2419         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
2420 
2421         final Categorizer quotaCategorizer = (userId, packageName, tag) -> {
2422             if (QUOTA_TRACKER_TIMEOUT_UIJ_TAG.equals(tag)) {
2423                 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC
2424                         ? QUOTA_TRACKER_CATEGORY_TIMEOUT_UIJ
2425                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2426             }
2427             if (QUOTA_TRACKER_TIMEOUT_EJ_TAG.equals(tag)) {
2428                 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC
2429                         ? QUOTA_TRACKER_CATEGORY_TIMEOUT_EJ
2430                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2431             }
2432             if (QUOTA_TRACKER_TIMEOUT_REG_TAG.equals(tag)) {
2433                 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC
2434                         ? QUOTA_TRACKER_CATEGORY_TIMEOUT_REG
2435                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2436             }
2437             if (QUOTA_TRACKER_TIMEOUT_TOTAL_TAG.equals(tag)) {
2438                 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC
2439                         ? QUOTA_TRACKER_CATEGORY_TIMEOUT_TOTAL
2440                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2441             }
2442             if (QUOTA_TRACKER_ANR_TAG.equals(tag)) {
2443                 return mConstants.ENABLE_EXECUTION_SAFEGUARDS_UDC
2444                         ? QUOTA_TRACKER_CATEGORY_ANR
2445                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2446             }
2447             if (QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG.equals(tag)) {
2448                 return mConstants.ENABLE_API_QUOTAS
2449                         ? QUOTA_TRACKER_CATEGORY_SCHEDULE_PERSISTED
2450                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2451             }
2452             if (QUOTA_TRACKER_SCHEDULE_LOGGED.equals(tag)) {
2453                 return mConstants.ENABLE_API_QUOTAS
2454                         ? QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED
2455                         : QUOTA_TRACKER_CATEGORY_DISABLED;
2456             }
2457             Slog.wtf(TAG, "Unexpected category tag: " + tag);
2458             return QUOTA_TRACKER_CATEGORY_DISABLED;
2459         };
2460         mQuotaTracker = new CountQuotaTracker(context, quotaCategorizer);
2461         updateQuotaTracker();
2462         // Log at most once per minute.
2463         // Set outside updateQuotaTracker() since this is intentionally not configurable.
2464         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_SCHEDULE_LOGGED, 1, 60_000);
2465         mQuotaTracker.setCountLimit(QUOTA_TRACKER_CATEGORY_DISABLED, Integer.MAX_VALUE, 60_000);
2466 
2467         mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
2468         mAppStandbyInternal.addListener(mStandbyTracker);
2469 
2470         // The job store needs to call back
2471         publishLocalService(JobSchedulerInternal.class, new LocalService());
2472 
2473         // Initialize the job store and set up any persisted jobs
2474         mJobStoreLoadedLatch = new CountDownLatch(1);
2475         mJobs = JobStore.get(this);
2476         mJobs.initAsync(mJobStoreLoadedLatch);
2477 
2478         mBatteryStateTracker = new BatteryStateTracker();
2479         mBatteryStateTracker.startTracking();
2480 
2481         // Create the controllers.
2482         mControllers = new ArrayList<StateController>();
2483         mPrefetchController = new PrefetchController(this);
2484         mControllers.add(mPrefetchController);
2485         final FlexibilityController flexibilityController =
2486                 new FlexibilityController(this, mPrefetchController);
2487         mControllers.add(flexibilityController);
2488         mConnectivityController =
2489                 new ConnectivityController(this, flexibilityController);
2490         mControllers.add(mConnectivityController);
2491         mControllers.add(new TimeController(this));
2492         final IdleController idleController = new IdleController(this, flexibilityController);
2493         mControllers.add(idleController);
2494         final BatteryController batteryController =
2495                 new BatteryController(this, flexibilityController);
2496         mControllers.add(batteryController);
2497         mStorageController = new StorageController(this);
2498         mControllers.add(mStorageController);
2499         final BackgroundJobsController backgroundJobsController =
2500                 new BackgroundJobsController(this);
2501         mControllers.add(backgroundJobsController);
2502         mControllers.add(new ContentObserverController(this));
2503         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
2504         mControllers.add(mDeviceIdleJobsController);
2505         mQuotaController =
2506                 new QuotaController(this, backgroundJobsController, mConnectivityController);
2507         mControllers.add(mQuotaController);
2508         mControllers.add(new ComponentController(this));
2509         mTareController =
2510                 new TareController(this, backgroundJobsController, mConnectivityController);
2511         mControllers.add(mTareController);
2512 
2513         mRestrictiveControllers = new ArrayList<>();
2514         mRestrictiveControllers.add(batteryController);
2515         mRestrictiveControllers.add(mConnectivityController);
2516         mRestrictiveControllers.add(idleController);
2517 
2518         // Create restrictions
2519         mJobRestrictions = new ArrayList<>();
2520         mJobRestrictions.add(new ThermalStatusRestriction(this));
2521 
2522         // If the job store determined that it can't yet reschedule persisted jobs,
2523         // we need to start watching the clock.
2524         if (!mJobs.jobTimesInflatedValid()) {
2525             Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
2526             context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
2527         }
2528     }
2529 
2530     private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
2531         @Override
2532         public void onReceive(Context context, Intent intent) {
2533             if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
2534                 // When we reach clock sanity, recalculate the temporal windows
2535                 // of all affected jobs.
2536                 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
2537                     Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
2538 
2539                     // We've done our job now, so stop watching the time.
2540                     context.unregisterReceiver(this);
2541 
2542                     // And kick off the work to update the affected jobs, using a secondary
2543                     // thread instead of chugging away here on the main looper thread.
2544                     mJobs.runWorkAsync(mJobTimeUpdater);
2545                 }
2546             }
2547         }
2548     };
2549 
2550     private final Runnable mJobTimeUpdater = () -> {
2551         Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND);
2552 
2553         final ArrayList<JobStatus> toRemove = new ArrayList<>();
2554         final ArrayList<JobStatus> toAdd = new ArrayList<>();
2555         synchronized (mLock) {
2556             // Note: we intentionally both look up the existing affected jobs and replace them
2557             // with recalculated ones inside the same lock lifetime.
2558             getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
2559 
2560             // Now, at each position [i], we have both the existing JobStatus
2561             // and the one that replaces it.
2562             final int N = toAdd.size();
2563             for (int i = 0; i < N; i++) {
2564                 final JobStatus oldJob = toRemove.get(i);
2565                 final JobStatus newJob = toAdd.get(i);
2566                 if (DEBUG) {
2567                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
2568                 }
2569                 cancelJobImplLocked(oldJob, newJob, JobParameters.STOP_REASON_SYSTEM_PROCESSING,
2570                         JobParameters.INTERNAL_STOP_REASON_RTC_UPDATED, "deferred rtc calculation");
2571             }
2572         }
2573     };
2574 
2575     @Override
2576     public void onStart() {
2577         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
2578     }
2579 
2580     @Override
2581     public void onBootPhase(int phase) {
2582         if (PHASE_LOCK_SETTINGS_READY == phase) {
2583             // This is the last phase before PHASE_SYSTEM_SERVICES_READY. We need to ensure that
2584             // persisted jobs are loaded before we can proceed to PHASE_SYSTEM_SERVICES_READY.
2585             try {
2586                 mJobStoreLoadedLatch.await();
2587             } catch (InterruptedException e) {
2588                 Slog.e(TAG, "Couldn't wait on job store loading latch");
2589             }
2590         } else if (PHASE_SYSTEM_SERVICES_READY == phase) {
2591             mConstantsObserver.start();
2592             for (StateController controller : mControllers) {
2593                 controller.onSystemServicesReady();
2594             }
2595 
2596             mAppStateTracker = (AppStateTrackerImpl) Objects.requireNonNull(
2597                     LocalServices.getService(AppStateTracker.class));
2598 
2599             LocalServices.getService(StorageManagerInternal.class)
2600                     .registerCloudProviderChangeListener(new CloudProviderChangeListener());
2601 
2602             // Register br for package removals and user removals.
2603             final IntentFilter filter = new IntentFilter();
2604             filter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
2605             filter.addAction(Intent.ACTION_PACKAGE_ADDED);
2606             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
2607             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
2608             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
2609             filter.addDataScheme("package");
2610             getContext().registerReceiverAsUser(
2611                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
2612             final IntentFilter uidFilter = new IntentFilter(Intent.ACTION_UID_REMOVED);
2613             getContext().registerReceiverAsUser(
2614                     mBroadcastReceiver, UserHandle.ALL, uidFilter, null, null);
2615             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
2616             userFilter.addAction(Intent.ACTION_USER_ADDED);
2617             getContext().registerReceiverAsUser(
2618                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
2619             try {
2620                 ActivityManager.getService().registerUidObserver(mUidObserver,
2621                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
2622                         | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
2623                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
2624             } catch (RemoteException e) {
2625                 // ignored; both services live in system_server
2626             }
2627 
2628             mConcurrencyManager.onSystemReady();
2629 
2630             // Remove any jobs that are not associated with any of the current users.
2631             cancelJobsForNonExistentUsers();
2632 
2633             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
2634                 mJobRestrictions.get(i).onSystemServicesReady();
2635             }
2636         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
2637             synchronized (mLock) {
2638                 // Let's go!
2639                 mReadyToRock = true;
2640                 mLocalDeviceIdleController =
2641                         LocalServices.getService(DeviceIdleInternal.class);
2642                 mConcurrencyManager.onThirdPartyAppsCanStart();
2643                 // Attach jobs to their controllers.
2644                 mJobs.forEachJob((job) -> {
2645                     for (int controller = 0; controller < mControllers.size(); controller++) {
2646                         final StateController sc = mControllers.get(controller);
2647                         sc.maybeStartTrackingJobLocked(job, null);
2648                     }
2649                 });
2650                 // GO GO GO!
2651                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2652             }
2653         }
2654     }
2655 
2656     /**
2657      * Called when we have a job status object that we need to insert in our
2658      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
2659      * about.
2660      */
2661     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
2662         if (!jobStatus.isPreparedLocked()) {
2663             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
2664         }
2665         jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
2666         final boolean update = lastJob != null;
2667         mJobs.add(jobStatus);
2668         // Clear potentially cached INVALID_JOB_ID reason.
2669         resetPendingJobReasonCache(jobStatus);
2670         if (mReadyToRock) {
2671             for (int i = 0; i < mControllers.size(); i++) {
2672                 StateController controller = mControllers.get(i);
2673                 if (update) {
2674                     controller.maybeStopTrackingJobLocked(jobStatus, null);
2675                 }
2676                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
2677             }
2678         }
2679     }
2680 
2681     /**
2682      * Called when we want to remove a JobStatus object that we've finished executing.
2683      *
2684      * @return true if the job was removed.
2685      */
2686     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
2687             boolean removeFromPersisted) {
2688         // Deal with any remaining work items in the old job.
2689         jobStatus.stopTrackingJobLocked(incomingJob);
2690 
2691         synchronized (mPendingJobReasonCache) {
2692             SparseIntArray reasonCache =
2693                     mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
2694             if (reasonCache != null) {
2695                 reasonCache.delete(jobStatus.getJobId());
2696             }
2697         }
2698 
2699         // Remove from store as well as controllers.
2700         final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
2701         if (!removed) {
2702             // We never create JobStatus objects for the express purpose of removing them, and this
2703             // method is only ever called for jobs that were saved in the JobStore at some point,
2704             // so if we can't find it, something may be wrong. As of Android T, there is a
2705             // legitimate code path where removed is false --- when an actively running job is
2706             // cancelled (eg. via JobScheduler.cancel() or the app scheduling a new job with the
2707             // same job ID), we remove it from the JobStore and tell the JobServiceContext to stop
2708             // running the job. Once the job stops running, we then call this method again.
2709             // TODO: rework code so we don't intentionally call this method twice for the same job
2710             Slog.w(TAG, "Job didn't exist in JobStore: " + jobStatus.toShortString());
2711         }
2712         if (mReadyToRock) {
2713             for (int i = 0; i < mControllers.size(); i++) {
2714                 StateController controller = mControllers.get(i);
2715                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob);
2716             }
2717         }
2718         return removed;
2719     }
2720 
2721     /** Remove the pending job reason for this job from the cache. */
2722     void resetPendingJobReasonCache(@NonNull JobStatus jobStatus) {
2723         synchronized (mPendingJobReasonCache) {
2724             final SparseIntArray reasons =
2725                     mPendingJobReasonCache.get(jobStatus.getUid(), jobStatus.getNamespace());
2726             if (reasons != null) {
2727                 reasons.delete(jobStatus.getJobId());
2728             }
2729         }
2730     }
2731 
2732     /** Return {@code true} if the specified job is currently executing. */
2733     @GuardedBy("mLock")
2734     public boolean isCurrentlyRunningLocked(JobStatus job) {
2735         return mConcurrencyManager.isJobRunningLocked(job);
2736     }
2737 
2738     /** @see JobConcurrencyManager#isJobInOvertimeLocked(JobStatus) */
2739     @GuardedBy("mLock")
2740     public boolean isJobInOvertimeLocked(JobStatus job) {
2741         return mConcurrencyManager.isJobInOvertimeLocked(job);
2742     }
2743 
2744     private void noteJobPending(JobStatus job) {
2745         mJobPackageTracker.notePending(job);
2746     }
2747 
2748     void noteJobsPending(List<JobStatus> jobs) {
2749         for (int i = jobs.size() - 1; i >= 0; i--) {
2750             noteJobPending(jobs.get(i));
2751         }
2752     }
2753 
2754     private void noteJobNonPending(JobStatus job) {
2755         mJobPackageTracker.noteNonpending(job);
2756     }
2757 
2758     private void clearPendingJobQueue() {
2759         JobStatus job;
2760         mPendingJobQueue.resetIterator();
2761         while ((job = mPendingJobQueue.next()) != null) {
2762             noteJobNonPending(job);
2763         }
2764         mPendingJobQueue.clear();
2765     }
2766 
2767     /**
2768      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
2769      * specify an override deadline on a failed job (the failed job will run even though it's not
2770      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
2771      * ready job with {@link JobStatus#getNumPreviousAttempts()} > 0 will be executed.
2772      *
2773      * @param failureToReschedule Provided job status that we will reschedule.
2774      * @return A newly instantiated JobStatus with the same constraints as the last job except
2775      * with adjusted timing constraints, or {@code null} if the job shouldn't be rescheduled for
2776      * some policy reason.
2777      * @see #maybeQueueReadyJobsForExecutionLocked
2778      */
2779     @Nullable
2780     @VisibleForTesting
2781     JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule,
2782             @JobParameters.StopReason int stopReason, int internalStopReason) {
2783         if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_USER_UI_STOP
2784                 && failureToReschedule.isUserVisibleJob()) {
2785             // If a user stops an app via Task Manager and the job was user-visible, then assume
2786             // the user wanted to stop that task and not let it run in the future. It's in the
2787             // app's best interests to provide action buttons in their notification to avoid this
2788             // scenario.
2789             Slog.i(TAG,
2790                     "Dropping " + failureToReschedule.toShortString() + " because of user stop");
2791             return null;
2792         }
2793 
2794         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
2795         final JobInfo job = failureToReschedule.getJob();
2796 
2797         final long initialBackoffMillis = job.getInitialBackoffMillis();
2798         int numFailures = failureToReschedule.getNumFailures();
2799         int numSystemStops = failureToReschedule.getNumSystemStops();
2800         // We should back off slowly if JobScheduler keeps stopping the job,
2801         // but back off immediately if the issue appeared to be the app's fault
2802         // or the user stopped the job somehow.
2803         if (internalStopReason == JobParameters.INTERNAL_STOP_REASON_SUCCESSFUL_FINISH
2804                 || internalStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
2805                 || internalStopReason == JobParameters.INTERNAL_STOP_REASON_ANR
2806                 || stopReason == JobParameters.STOP_REASON_USER) {
2807             numFailures++;
2808         } else {
2809             numSystemStops++;
2810         }
2811         final int backoffAttempts =
2812                 numFailures + numSystemStops / mConstants.SYSTEM_STOP_TO_FAILURE_RATIO;
2813         final long earliestRuntimeMs;
2814 
2815         if (backoffAttempts == 0) {
2816             earliestRuntimeMs = JobStatus.NO_EARLIEST_RUNTIME;
2817         } else {
2818             long delayMillis;
2819             switch (job.getBackoffPolicy()) {
2820                 case JobInfo.BACKOFF_POLICY_LINEAR: {
2821                     long backoff = initialBackoffMillis;
2822                     if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME_MS) {
2823                         backoff = mConstants.MIN_LINEAR_BACKOFF_TIME_MS;
2824                     }
2825                     delayMillis = backoff * backoffAttempts;
2826                 }
2827                 break;
2828                 default:
2829                     if (DEBUG) {
2830                         Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
2831                     }
2832                     // Intentional fallthrough.
2833                 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
2834                     long backoff = initialBackoffMillis;
2835                     if (backoff < mConstants.MIN_EXP_BACKOFF_TIME_MS) {
2836                         backoff = mConstants.MIN_EXP_BACKOFF_TIME_MS;
2837                     }
2838                     delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
2839                 }
2840                 break;
2841             }
2842             delayMillis =
2843                     Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
2844             earliestRuntimeMs = elapsedNowMillis + delayMillis;
2845         }
2846         JobStatus newJob = new JobStatus(failureToReschedule,
2847                 earliestRuntimeMs,
2848                 JobStatus.NO_LATEST_RUNTIME, numFailures, numSystemStops,
2849                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis(),
2850                 failureToReschedule.getCumulativeExecutionTimeMs());
2851         if (stopReason == JobParameters.STOP_REASON_USER) {
2852             // Demote all jobs to regular for user stops so they don't keep privileges.
2853             newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_USER);
2854         }
2855         if (newJob.getCumulativeExecutionTimeMs() >= mConstants.RUNTIME_CUMULATIVE_UI_LIMIT_MS
2856                 && newJob.shouldTreatAsUserInitiatedJob()) {
2857             newJob.addInternalFlags(JobStatus.INTERNAL_FLAG_DEMOTED_BY_SYSTEM_UIJ);
2858         }
2859         if (job.isPeriodic()) {
2860             newJob.setOriginalLatestRunTimeElapsed(
2861                     failureToReschedule.getOriginalLatestRunTimeElapsed());
2862         }
2863         for (int ic = 0; ic < mControllers.size(); ic++) {
2864             StateController controller = mControllers.get(ic);
2865             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
2866         }
2867         return newJob;
2868     }
2869 
2870     /**
2871      * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
2872      * does not cause a job's period to be larger than requested (eg: if the requested period is
2873      * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
2874      * and try to optimize scheduling if the current job finished less than this amount of time to
2875      * the start of the next period
2876      */
2877     private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
2878 
2879     /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
2880     public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
2881 
2882     /**
2883      * Called after a periodic has executed so we can reschedule it. We take the last execution
2884      * time of the job to be the time of completion (i.e. the time at which this function is
2885      * called).
2886      * <p>This could be inaccurate b/c the job can run for as long as
2887      * {@link Constants#DEFAULT_RUNTIME_FREE_QUOTA_MAX_LIMIT_MS}, but
2888      * will lead to underscheduling at least, rather than if we had taken the last execution time
2889      * to be the start of the execution.
2890      *
2891      * @return A new job representing the execution criteria for this instantiation of the
2892      * recurring job.
2893      */
2894     @VisibleForTesting
2895     JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
2896         final long elapsedNow = sElapsedRealtimeClock.millis();
2897         final long newLatestRuntimeElapsed;
2898         // Make sure period is in the interval [min_possible_period, max_possible_period].
2899         final long period = Math.max(JobInfo.getMinPeriodMillis(),
2900                 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
2901         // Make sure flex is in the interval [min_possible_flex, period].
2902         final long flex = Math.max(JobInfo.getMinFlexMillis(),
2903                 Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
2904         long rescheduleBuffer = 0;
2905 
2906         long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
2907         if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
2908             Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
2909             olrte = elapsedNow;
2910         }
2911         final long latestRunTimeElapsed = olrte;
2912 
2913         final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
2914         if (elapsedNow > latestRunTimeElapsed) {
2915             // The job ran past its expected run window. Have it count towards the current window
2916             // and schedule a new job for the next window.
2917             if (DEBUG) {
2918                 Slog.i(TAG, "Periodic job ran after its intended window by " + diffMs + " ms");
2919             }
2920             long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
2921             // Determine how far into a single period the job ran, and determine if it's too close
2922             // to the start of the next period. If the difference between the start of the execution
2923             // window and the previous execution time inside of the period is less than the
2924             // threshold, then we say that the job ran too close to the next period.
2925             if (period != flex && (period - flex - (diffMs % period)) <= flex / 6) {
2926                 if (DEBUG) {
2927                     Slog.d(TAG, "Custom flex job ran too close to next window.");
2928                 }
2929                 // For custom flex periods, if the job was run too close to the next window,
2930                 // skip the next window and schedule for the following one.
2931                 numSkippedWindows += 1;
2932             }
2933             newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
2934         } else {
2935             newLatestRuntimeElapsed = latestRunTimeElapsed + period;
2936             if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
2937                 // Add a little buffer to the start of the next window so the job doesn't run
2938                 // too soon after this completed one.
2939                 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
2940             }
2941         }
2942 
2943         if (newLatestRuntimeElapsed < elapsedNow) {
2944             Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
2945                     + newLatestRuntimeElapsed);
2946             return new JobStatus(periodicToReschedule,
2947                     elapsedNow + period - flex, elapsedNow + period,
2948                     0 /* numFailures */, 0 /* numSystemStops */,
2949                     sSystemClock.millis() /* lastSuccessfulRunTime */,
2950                     periodicToReschedule.getLastFailedRunTime(),
2951                     0 /* Reset cumulativeExecutionTime because of successful execution */);
2952         }
2953 
2954         final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
2955                 - Math.min(flex, period - rescheduleBuffer);
2956 
2957         if (DEBUG) {
2958             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
2959                     newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
2960                     + "]s");
2961         }
2962         return new JobStatus(periodicToReschedule,
2963                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
2964                 0 /* numFailures */, 0 /* numSystemStops */,
2965                 sSystemClock.millis() /* lastSuccessfulRunTime */,
2966                 periodicToReschedule.getLastFailedRunTime(),
2967                 0 /* Reset cumulativeExecutionTime because of successful execution */);
2968     }
2969 
2970     @VisibleForTesting
2971     void maybeProcessBuggyJob(@NonNull JobStatus jobStatus, int debugStopReason) {
2972         boolean jobTimedOut = debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT;
2973         // If madeActive = 0, the job never actually started.
2974         if (!jobTimedOut && jobStatus.madeActive > 0) {
2975             final long executionDurationMs = sUptimeMillisClock.millis() - jobStatus.madeActive;
2976             // The debug reason may be different if we stopped the job for some other reason
2977             // (eg. constraints), so look at total execution time to be safe.
2978             if (jobStatus.startedAsUserInitiatedJob) {
2979                 // TODO: factor in different min guarantees for different UI job types
2980                 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_UI_GUARANTEE_MS;
2981             } else if (jobStatus.startedAsExpeditedJob) {
2982                 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS;
2983             } else {
2984                 jobTimedOut = executionDurationMs >= mConstants.RUNTIME_MIN_GUARANTEE_MS;
2985             }
2986         }
2987         if (jobTimedOut) {
2988             final int userId = jobStatus.getTimeoutBlameUserId();
2989             final String pkg = jobStatus.getTimeoutBlamePackageName();
2990             mQuotaTracker.noteEvent(userId, pkg,
2991                     jobStatus.startedAsUserInitiatedJob
2992                             ? QUOTA_TRACKER_TIMEOUT_UIJ_TAG
2993                             : (jobStatus.startedAsExpeditedJob
2994                                     ? QUOTA_TRACKER_TIMEOUT_EJ_TAG
2995                                     : QUOTA_TRACKER_TIMEOUT_REG_TAG));
2996             if (!mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_TIMEOUT_TOTAL_TAG)) {
2997                 mAppStandbyInternal.restrictApp(
2998                         pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
2999             }
3000         }
3001 
3002         if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_ANR) {
3003             final int callingUserId = jobStatus.getUserId();
3004             final String callingPkg = jobStatus.getServiceComponent().getPackageName();
3005             if (!mQuotaTracker.noteEvent(callingUserId, callingPkg, QUOTA_TRACKER_ANR_TAG)) {
3006                 mAppStandbyInternal.restrictApp(callingPkg, callingUserId,
3007                         UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
3008             }
3009         }
3010     }
3011 
3012     // JobCompletedListener implementations.
3013 
3014     /**
3015      * A job just finished executing. We fetch the
3016      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
3017      * whether we want to reschedule we re-add it to the controllers.
3018      *
3019      * @param jobStatus       Completed job.
3020      * @param needsReschedule Whether the implementing class should reschedule this job.
3021      */
3022     @Override
3023     public void onJobCompletedLocked(JobStatus jobStatus, @JobParameters.StopReason int stopReason,
3024             int debugStopReason, boolean needsReschedule) {
3025         if (DEBUG) {
3026             Slog.d(TAG, "Completed " + jobStatus + ", reason=" + debugStopReason
3027                     + ", reschedule=" + needsReschedule);
3028         }
3029 
3030         mLastCompletedJobs[mLastCompletedJobIndex] = jobStatus;
3031         mLastCompletedJobTimeElapsed[mLastCompletedJobIndex] = sElapsedRealtimeClock.millis();
3032         mLastCompletedJobIndex = (mLastCompletedJobIndex + 1) % NUM_COMPLETED_JOB_HISTORY;
3033 
3034         maybeProcessBuggyJob(jobStatus, debugStopReason);
3035 
3036         if (debugStopReason == JobParameters.INTERNAL_STOP_REASON_UNINSTALL
3037                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_DATA_CLEARED) {
3038             // The job should have already been cleared from the rest of the JS tracking. No need
3039             // to go through all that flow again.
3040             jobStatus.unprepareLocked();
3041             reportActiveLocked();
3042             return;
3043         }
3044 
3045         // Intentionally not checking expedited job quota here. An app can't find out if it's run
3046         // out of quota when it asks JS to reschedule an expedited job. Instead, the rescheduled
3047         // EJ will just be demoted to a regular job if the app has no EJ quota left.
3048 
3049         // If the job wants to be rescheduled, we first need to make the next upcoming
3050         // job so we can transfer any appropriate state over from the previous job when
3051         // we stop it.
3052         final JobStatus rescheduledJob = needsReschedule
3053                 ? getRescheduleJobForFailureLocked(jobStatus, stopReason, debugStopReason) : null;
3054         if (rescheduledJob != null
3055                 && !rescheduledJob.shouldTreatAsUserInitiatedJob()
3056                 && (debugStopReason == JobParameters.INTERNAL_STOP_REASON_TIMEOUT
3057                 || debugStopReason == JobParameters.INTERNAL_STOP_REASON_PREEMPT)) {
3058             rescheduledJob.disallowRunInBatterySaverAndDoze();
3059         }
3060 
3061         // Do not write back immediately if this is a periodic job. The job may get lost if system
3062         // shuts down before it is added back.
3063         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
3064             if (DEBUG) {
3065                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
3066             }
3067             JobStatus newJs = mJobs.getJobByUidAndJobId(
3068                     jobStatus.getUid(), jobStatus.getNamespace(), jobStatus.getJobId());
3069             if (newJs != null) {
3070                 // This job was stopped because the app scheduled a new job with the same job ID.
3071                 // Check if the new job is ready to run.
3072                 mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, newJs).sendToTarget();
3073             }
3074             return;
3075         }
3076 
3077         if (rescheduledJob != null) {
3078             try {
3079                 rescheduledJob.prepareLocked();
3080             } catch (SecurityException e) {
3081                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
3082             }
3083             startTrackingJobLocked(rescheduledJob, jobStatus);
3084         } else if (jobStatus.getJob().isPeriodic()) {
3085             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
3086             try {
3087                 rescheduledPeriodic.prepareLocked();
3088             } catch (SecurityException e) {
3089                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
3090             }
3091             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
3092         }
3093         jobStatus.unprepareLocked();
3094         reportActiveLocked();
3095     }
3096 
3097     // StateChangedListener implementations.
3098 
3099     /**
3100      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} to run
3101      * through a list of jobs and start/stop any whose status has changed.
3102      */
3103     @Override
3104     public void onControllerStateChanged(@Nullable ArraySet<JobStatus> changedJobs) {
3105         if (changedJobs == null) {
3106             mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
3107             synchronized (mPendingJobReasonCache) {
3108                 mPendingJobReasonCache.clear();
3109             }
3110         } else if (changedJobs.size() > 0) {
3111             synchronized (mLock) {
3112                 mChangedJobList.addAll(changedJobs);
3113             }
3114             mHandler.obtainMessage(MSG_CHECK_CHANGED_JOB_LIST).sendToTarget();
3115             synchronized (mPendingJobReasonCache) {
3116                 for (int i = changedJobs.size() - 1; i >= 0; --i) {
3117                     final JobStatus job = changedJobs.valueAt(i);
3118                     resetPendingJobReasonCache(job);
3119                 }
3120             }
3121         }
3122     }
3123 
3124     @Override
3125     public void onRestrictionStateChanged(@NonNull JobRestriction restriction,
3126             boolean stopOvertimeJobs) {
3127         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
3128         if (stopOvertimeJobs) {
3129             synchronized (mLock) {
3130                 mConcurrencyManager.maybeStopOvertimeJobsLocked(restriction);
3131             }
3132         }
3133     }
3134 
3135     @Override
3136     public void onRunJobNow(JobStatus jobStatus) {
3137         if (jobStatus == null) {
3138             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
3139         } else {
3140             mHandler.obtainMessage(MSG_CHECK_INDIVIDUAL_JOB, jobStatus).sendToTarget();
3141         }
3142     }
3143 
3144     final private class JobHandler extends Handler {
3145 
3146         public JobHandler(Looper looper) {
3147             super(looper);
3148         }
3149 
3150         @Override
3151         public void handleMessage(Message message) {
3152             synchronized (mLock) {
3153                 if (!mReadyToRock) {
3154                     return;
3155                 }
3156                 switch (message.what) {
3157                     case MSG_CHECK_INDIVIDUAL_JOB: {
3158                         JobStatus js = (JobStatus) message.obj;
3159                         if (js != null) {
3160                             if (isReadyToBeExecutedLocked(js)) {
3161                                 mJobPackageTracker.notePending(js);
3162                                 mPendingJobQueue.add(js);
3163                             }
3164                             mChangedJobList.remove(js);
3165                         } else {
3166                             Slog.e(TAG, "Given null job to check individually");
3167                         }
3168                     } break;
3169                     case MSG_CHECK_JOB:
3170                         if (DEBUG) {
3171                             Slog.d(TAG, "MSG_CHECK_JOB");
3172                         }
3173                         if (mReportedActive) {
3174                             // if jobs are currently being run, queue all ready jobs for execution.
3175                             queueReadyJobsForExecutionLocked();
3176                         } else {
3177                             // Check the list of jobs and run some of them if we feel inclined.
3178                             maybeQueueReadyJobsForExecutionLocked();
3179                         }
3180                         break;
3181                     case MSG_CHECK_JOB_GREEDY:
3182                         if (DEBUG) {
3183                             Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
3184                         }
3185                         queueReadyJobsForExecutionLocked();
3186                         break;
3187                     case MSG_CHECK_CHANGED_JOB_LIST:
3188                         if (DEBUG) {
3189                             Slog.d(TAG, "MSG_CHECK_CHANGED_JOB_LIST");
3190                         }
3191                         checkChangedJobListLocked();
3192                         break;
3193                     case MSG_STOP_JOB:
3194                         cancelJobImplLocked((JobStatus) message.obj, null, message.arg1,
3195                                 JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
3196                                 "app no longer allowed to run");
3197                         break;
3198 
3199                     case MSG_UID_STATE_CHANGED: {
3200                         final SomeArgs args = (SomeArgs) message.obj;
3201                         final int uid = args.argi1;
3202                         final int procState = args.argi2;
3203                         final int capabilities = args.argi3;
3204                         updateUidState(uid, procState, capabilities);
3205                         args.recycle();
3206                         break;
3207                     }
3208                     case MSG_UID_GONE: {
3209                         final int uid = message.arg1;
3210                         final boolean disabled = message.arg2 != 0;
3211                         updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
3212                                 ActivityManager.PROCESS_CAPABILITY_NONE);
3213                         if (disabled) {
3214                             cancelJobsForUid(uid,
3215                                     /* includeSourceApp */ true,
3216                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
3217                                     JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
3218                                     "uid gone");
3219                         }
3220                         synchronized (mLock) {
3221                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
3222                         }
3223                         break;
3224                     }
3225                     case MSG_UID_ACTIVE: {
3226                         final int uid = message.arg1;
3227                         synchronized (mLock) {
3228                             mDeviceIdleJobsController.setUidActiveLocked(uid, true);
3229                         }
3230                         break;
3231                     }
3232                     case MSG_UID_IDLE: {
3233                         final int uid = message.arg1;
3234                         final boolean disabled = message.arg2 != 0;
3235                         if (disabled) {
3236                             cancelJobsForUid(uid,
3237                                     /* includeSourceApp */ true,
3238                                     JobParameters.STOP_REASON_BACKGROUND_RESTRICTION,
3239                                     JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED,
3240                                     "app uid idle");
3241                         }
3242                         synchronized (mLock) {
3243                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
3244                         }
3245                         break;
3246                     }
3247 
3248                     case MSG_CHECK_MEDIA_EXEMPTION: {
3249                         final SomeArgs args = (SomeArgs) message.obj;
3250                         synchronized (mLock) {
3251                             updateMediaBackupExemptionLocked(
3252                                     args.argi1, (String) args.arg1, (String) args.arg2);
3253                         }
3254                         args.recycle();
3255                         break;
3256                     }
3257 
3258                     case MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS: {
3259                         final IUserVisibleJobObserver observer =
3260                                 (IUserVisibleJobObserver) message.obj;
3261                         synchronized (mLock) {
3262                             for (int i = mConcurrencyManager.mActiveServices.size() - 1; i >= 0;
3263                                     --i) {
3264                                 JobServiceContext context =
3265                                         mConcurrencyManager.mActiveServices.get(i);
3266                                 final JobStatus jobStatus = context.getRunningJobLocked();
3267                                 if (jobStatus != null && jobStatus.isUserVisibleJob()) {
3268                                     try {
3269                                         observer.onUserVisibleJobStateChanged(
3270                                                 jobStatus.getUserVisibleJobSummary(),
3271                                                 /* isRunning */ true);
3272                                     } catch (RemoteException e) {
3273                                         // Will be unregistered automatically by
3274                                         // RemoteCallbackList's dead-object tracking,
3275                                         // so don't need to remove it here.
3276                                         break;
3277                                     }
3278                                 }
3279                             }
3280                         }
3281                         break;
3282                     }
3283 
3284                     case MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE: {
3285                         final SomeArgs args = (SomeArgs) message.obj;
3286                         final JobServiceContext context = (JobServiceContext) args.arg1;
3287                         final JobStatus jobStatus = (JobStatus) args.arg2;
3288                         final UserVisibleJobSummary summary = jobStatus.getUserVisibleJobSummary();
3289                         final boolean isRunning = args.argi1 == 1;
3290                         for (int i = mUserVisibleJobObservers.beginBroadcast() - 1; i >= 0; --i) {
3291                             try {
3292                                 mUserVisibleJobObservers.getBroadcastItem(i)
3293                                         .onUserVisibleJobStateChanged(summary, isRunning);
3294                             } catch (RemoteException e) {
3295                                 // Will be unregistered automatically by RemoteCallbackList's
3296                                 // dead-object tracking, so nothing we need to do here.
3297                             }
3298                         }
3299                         mUserVisibleJobObservers.finishBroadcast();
3300                         args.recycle();
3301                         break;
3302                     }
3303                 }
3304                 maybeRunPendingJobsLocked();
3305             }
3306         }
3307     }
3308 
3309     /**
3310      * Check if a job is restricted by any of the declared {@link JobRestriction JobRestrictions}.
3311      * Note, that the jobs with {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias or higher may not
3312      * be restricted, thus we won't even perform the check, but simply return null early.
3313      *
3314      * @param job to be checked
3315      * @return the first {@link JobRestriction} restricting the given job that has been found; null
3316      * - if passes all the restrictions or has {@link JobInfo#BIAS_FOREGROUND_SERVICE} bias
3317      * or higher.
3318      */
3319     @GuardedBy("mLock")
3320     JobRestriction checkIfRestricted(JobStatus job) {
3321         if (evaluateJobBiasLocked(job) >= JobInfo.BIAS_FOREGROUND_SERVICE) {
3322             // Jobs with BIAS_FOREGROUND_SERVICE or higher should not be restricted
3323             return null;
3324         }
3325         for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3326             final JobRestriction restriction = mJobRestrictions.get(i);
3327             if (restriction.isJobRestricted(job)) {
3328                 return restriction;
3329             }
3330         }
3331         return null;
3332     }
3333 
3334     @GuardedBy("mLock")
3335     private void stopNonReadyActiveJobsLocked() {
3336         mConcurrencyManager.stopNonReadyActiveJobsLocked();
3337     }
3338 
3339     /**
3340      * Run through list of jobs and execute all possible - at least one is expired so we do
3341      * as many as we can.
3342      */
3343     @GuardedBy("mLock")
3344     private void queueReadyJobsForExecutionLocked() {
3345         // This method will check and capture all ready jobs, so we don't need to keep any messages
3346         // in the queue.
3347         mHandler.removeMessages(MSG_CHECK_JOB_GREEDY);
3348         mHandler.removeMessages(MSG_CHECK_INDIVIDUAL_JOB);
3349         // MSG_CHECK_JOB is a weaker form of _GREEDY. Since we're checking and queueing all ready
3350         // jobs, we don't need to keep any MSG_CHECK_JOB messages in the queue.
3351         mHandler.removeMessages(MSG_CHECK_JOB);
3352         // MSG_CHECK_CHANGED_JOB_LIST is a weaker form of _GREEDY. Since we're checking and queueing
3353         // all ready jobs, we don't need to keep any MSG_CHECK_CHANGED_JOB_LIST messages in the
3354         // queue.
3355         mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
3356         mChangedJobList.clear();
3357         if (DEBUG) {
3358             Slog.d(TAG, "queuing all ready jobs for execution:");
3359         }
3360         clearPendingJobQueue();
3361         stopNonReadyActiveJobsLocked();
3362         mJobs.forEachJob(mReadyQueueFunctor);
3363         mReadyQueueFunctor.postProcessLocked();
3364 
3365         if (DEBUG) {
3366             final int queuedJobs = mPendingJobQueue.size();
3367             if (queuedJobs == 0) {
3368                 Slog.d(TAG, "No jobs pending.");
3369             } else {
3370                 Slog.d(TAG, queuedJobs + " jobs queued.");
3371             }
3372         }
3373     }
3374 
3375     final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
3376         final ArrayList<JobStatus> newReadyJobs = new ArrayList<>();
3377 
3378         @Override
3379         public void accept(JobStatus job) {
3380             if (isReadyToBeExecutedLocked(job)) {
3381                 if (DEBUG) {
3382                     Slog.d(TAG, "    queued " + job.toShortString());
3383                 }
3384                 newReadyJobs.add(job);
3385             }
3386         }
3387 
3388         @GuardedBy("mLock")
3389         private void postProcessLocked() {
3390             noteJobsPending(newReadyJobs);
3391             mPendingJobQueue.addAll(newReadyJobs);
3392 
3393             newReadyJobs.clear();
3394         }
3395     }
3396 
3397     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
3398 
3399     /**
3400      * The state of at least one job has changed. Here is where we could enforce various
3401      * policies on when we want to execute jobs.
3402      */
3403     final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
3404         int forceBatchedCount;
3405         int unbatchedCount;
3406         final List<JobStatus> runnableJobs = new ArrayList<>();
3407 
3408         public MaybeReadyJobQueueFunctor() {
3409             reset();
3410         }
3411 
3412         @Override
3413         public void accept(JobStatus job) {
3414             final boolean isRunning = isCurrentlyRunningLocked(job);
3415             if (isReadyToBeExecutedLocked(job, false)) {
3416                 if (mActivityManagerInternal.isAppStartModeDisabled(job.getUid(),
3417                         job.getJob().getService().getPackageName())) {
3418                     Slog.w(TAG, "Aborting job " + job.getUid() + ":"
3419                             + job.getJob().toString() + " -- package not allowed to start");
3420                     if (isRunning) {
3421                         mHandler.obtainMessage(MSG_STOP_JOB,
3422                                 JobParameters.STOP_REASON_BACKGROUND_RESTRICTION, 0, job)
3423                                 .sendToTarget();
3424                     } else if (mPendingJobQueue.remove(job)) {
3425                         noteJobNonPending(job);
3426                     }
3427                     return;
3428                 }
3429 
3430                 final boolean shouldForceBatchJob;
3431                 if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
3432                     // Never batch expedited or user-initiated jobs, even for RESTRICTED apps.
3433                     shouldForceBatchJob = false;
3434                 } else if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
3435                     // Restricted jobs must always be batched
3436                     shouldForceBatchJob = true;
3437                 } else if (job.getJob().isPrefetch()) {
3438                     // Only relax batching on prefetch jobs if we expect the app to be launched
3439                     // relatively soon. PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS defines what
3440                     // "relatively soon" means.
3441                     final long relativelySoonCutoffTime = sSystemClock.millis()
3442                             + mConstants.PREFETCH_FORCE_BATCH_RELAX_THRESHOLD_MS;
3443                     shouldForceBatchJob =
3444                             mPrefetchController.getNextEstimatedLaunchTimeLocked(job)
3445                                     > relativelySoonCutoffTime;
3446                 } else if (job.getNumPreviousAttempts() > 0) {
3447                     shouldForceBatchJob = false;
3448                 } else {
3449                     final long nowElapsed = sElapsedRealtimeClock.millis();
3450                     final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0
3451                             && nowElapsed - job.getFirstForceBatchedTimeElapsed()
3452                             >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
3453                     shouldForceBatchJob =
3454                             mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
3455                                     && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
3456                                     && job.getEffectiveStandbyBucket() != EXEMPTED_INDEX
3457                                     && !batchDelayExpired;
3458                 }
3459 
3460                 if (shouldForceBatchJob) {
3461                     // Force batching non-ACTIVE jobs. Don't include them in the other counts.
3462                     forceBatchedCount++;
3463                     if (job.getFirstForceBatchedTimeElapsed() == 0) {
3464                         job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis());
3465                     }
3466                 } else {
3467                     unbatchedCount++;
3468                 }
3469                 if (!isRunning) {
3470                     runnableJobs.add(job);
3471                 }
3472             } else {
3473                 if (isRunning) {
3474                     final int internalStopReason;
3475                     final String debugReason;
3476                     if (!job.isReady()) {
3477                         if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX
3478                                 && job.getStopReason() == JobParameters.STOP_REASON_APP_STANDBY) {
3479                             internalStopReason =
3480                                     JobParameters.INTERNAL_STOP_REASON_RESTRICTED_BUCKET;
3481                             debugReason = "cancelled due to restricted bucket";
3482                         } else {
3483                             internalStopReason =
3484                                     JobParameters.INTERNAL_STOP_REASON_CONSTRAINTS_NOT_SATISFIED;
3485                             debugReason = "cancelled due to unsatisfied constraints";
3486                         }
3487                     } else {
3488                         final JobRestriction restriction = checkIfRestricted(job);
3489                         if (restriction != null) {
3490                             internalStopReason = restriction.getInternalReason();
3491                             debugReason = "restricted due to "
3492                                     + JobParameters.getInternalReasonCodeDescription(
3493                                     internalStopReason);
3494                         } else {
3495                             internalStopReason = JobParameters.INTERNAL_STOP_REASON_UNKNOWN;
3496                             debugReason = "couldn't figure out why the job should stop running";
3497                         }
3498                     }
3499                     mConcurrencyManager.stopJobOnServiceContextLocked(job, job.getStopReason(),
3500                             internalStopReason, debugReason);
3501                 } else if (mPendingJobQueue.remove(job)) {
3502                     noteJobNonPending(job);
3503                 }
3504             }
3505         }
3506 
3507         @GuardedBy("mLock")
3508         @VisibleForTesting
3509         void postProcessLocked() {
3510             if (unbatchedCount > 0
3511                     || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) {
3512                 if (DEBUG) {
3513                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
3514                 }
3515                 noteJobsPending(runnableJobs);
3516                 mPendingJobQueue.addAll(runnableJobs);
3517             } else {
3518                 if (DEBUG) {
3519                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
3520                 }
3521                 final int numRunnableJobs = runnableJobs.size();
3522                 if (numRunnableJobs > 0) {
3523                     synchronized (mPendingJobReasonCache) {
3524                         for (int i = 0; i < numRunnableJobs; ++i) {
3525                             final JobStatus job = runnableJobs.get(i);
3526                             SparseIntArray reasons =
3527                                     mPendingJobReasonCache.get(job.getUid(), job.getNamespace());
3528                             if (reasons == null) {
3529                                 reasons = new SparseIntArray();
3530                                 mPendingJobReasonCache
3531                                         .add(job.getUid(), job.getNamespace(), reasons);
3532                             }
3533                             // We're force batching these jobs, so consider it an optimization
3534                             // policy reason.
3535                             reasons.put(job.getJobId(),
3536                                     JobScheduler.PENDING_JOB_REASON_JOB_SCHEDULER_OPTIMIZATION);
3537                         }
3538                     }
3539                 }
3540             }
3541 
3542             // Be ready for next time
3543             reset();
3544         }
3545 
3546         @VisibleForTesting
3547         void reset() {
3548             forceBatchedCount = 0;
3549             unbatchedCount = 0;
3550             runnableJobs.clear();
3551         }
3552     }
3553 
3554     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
3555 
3556     @GuardedBy("mLock")
3557     private void maybeQueueReadyJobsForExecutionLocked() {
3558         mHandler.removeMessages(MSG_CHECK_JOB);
3559         // This method will evaluate all jobs, so we don't need to keep any messages for a subset
3560         // of jobs in the queue.
3561         mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
3562         mChangedJobList.clear();
3563         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
3564 
3565         clearPendingJobQueue();
3566         stopNonReadyActiveJobsLocked();
3567         mJobs.forEachJob(mMaybeQueueFunctor);
3568         mMaybeQueueFunctor.postProcessLocked();
3569     }
3570 
3571     @GuardedBy("mLock")
3572     private void checkChangedJobListLocked() {
3573         mHandler.removeMessages(MSG_CHECK_CHANGED_JOB_LIST);
3574         if (DEBUG) {
3575             Slog.d(TAG, "Check changed jobs...");
3576         }
3577         if (mChangedJobList.size() == 0) {
3578             return;
3579         }
3580 
3581         mChangedJobList.forEach(mMaybeQueueFunctor);
3582         mMaybeQueueFunctor.postProcessLocked();
3583         mChangedJobList.clear();
3584     }
3585 
3586     @GuardedBy("mLock")
3587     private void updateMediaBackupExemptionLocked(int userId, @Nullable String oldPkg,
3588             @Nullable String newPkg) {
3589         final Predicate<JobStatus> shouldProcessJob =
3590                 (job) -> job.getSourceUserId() == userId
3591                         && (job.getSourcePackageName().equals(oldPkg)
3592                         || job.getSourcePackageName().equals(newPkg));
3593         mJobs.forEachJob(shouldProcessJob,
3594                 (job) -> {
3595                     if (job.updateMediaBackupExemptionStatus()) {
3596                         mChangedJobList.add(job);
3597                     }
3598                 });
3599         mHandler.sendEmptyMessage(MSG_CHECK_CHANGED_JOB_LIST);
3600     }
3601 
3602     /** Returns true if both the calling and source users for the job are started. */
3603     @GuardedBy("mLock")
3604     public boolean areUsersStartedLocked(final JobStatus job) {
3605         boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
3606         if (job.getUserId() == job.getSourceUserId()) {
3607             return sourceStarted;
3608         }
3609         return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
3610     }
3611 
3612     /**
3613      * Criteria for moving a job into the pending queue:
3614      *      - It's ready.
3615      *      - It's not pending.
3616      *      - It's not already running on a JSC.
3617      *      - The user that requested the job is running.
3618      *      - The job's standby bucket has come due to be runnable.
3619      *      - The component is enabled and runnable.
3620      */
3621     @VisibleForTesting
3622     @GuardedBy("mLock")
3623     boolean isReadyToBeExecutedLocked(JobStatus job) {
3624         return isReadyToBeExecutedLocked(job, true);
3625     }
3626 
3627     @GuardedBy("mLock")
3628     boolean isReadyToBeExecutedLocked(JobStatus job, boolean rejectActive) {
3629         final boolean jobReady = job.isReady() || evaluateControllerStatesLocked(job);
3630 
3631         if (DEBUG) {
3632             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
3633                     + " ready=" + jobReady);
3634         }
3635 
3636         // This is a condition that is very likely to be false (most jobs that are
3637         // scheduled are sitting there, not ready yet) and very cheap to check (just
3638         // a few conditions on data in JobStatus).
3639         if (!jobReady) {
3640             if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
3641                 Slog.v(TAG, "    NOT READY: " + job);
3642             }
3643             return false;
3644         }
3645 
3646         final boolean jobExists = mJobs.containsJob(job);
3647         final boolean userStarted = areUsersStartedLocked(job);
3648         final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
3649 
3650         if (DEBUG) {
3651             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
3652                     + " exists=" + jobExists + " userStarted=" + userStarted
3653                     + " backingUp=" + backingUp);
3654         }
3655 
3656         // These are also fairly cheap to check, though they typically will not
3657         // be conditions we fail.
3658         if (!jobExists || !userStarted || backingUp) {
3659             return false;
3660         }
3661 
3662         if (checkIfRestricted(job) != null) {
3663             return false;
3664         }
3665 
3666         final boolean jobPending = mPendingJobQueue.contains(job);
3667         final boolean jobActive = rejectActive && mConcurrencyManager.isJobRunningLocked(job);
3668 
3669         if (DEBUG) {
3670             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
3671                     + " pending=" + jobPending + " active=" + jobActive);
3672         }
3673 
3674         // These can be a little more expensive (especially jobActive, since we need to
3675         // go through the array of all potentially active jobs), so we are doing them
3676         // later...  but still before checking with the package manager!
3677         if (jobPending || jobActive) {
3678             return false;
3679         }
3680 
3681         // Validate that the defined package+service is still present & viable.
3682         return isComponentUsable(job);
3683     }
3684 
3685     private boolean isComponentUsable(@NonNull JobStatus job) {
3686         final String processName = job.serviceProcessName;
3687 
3688         if (processName == null) {
3689             if (DEBUG) {
3690                 Slog.v(TAG, "isComponentUsable: " + job.toShortString()
3691                         + " component not present");
3692             }
3693             return false;
3694         }
3695 
3696         // Everything else checked out so far, so this is the final yes/no check
3697         final boolean appIsBad = mActivityManagerInternal.isAppBad(processName, job.getUid());
3698         if (DEBUG && appIsBad) {
3699             Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
3700         }
3701         return !appIsBad;
3702     }
3703 
3704     /**
3705      * Gets each controller to evaluate the job's state
3706      * and then returns the value of {@link JobStatus#isReady()}.
3707      */
3708     @VisibleForTesting
3709     boolean evaluateControllerStatesLocked(final JobStatus job) {
3710         for (int c = mControllers.size() - 1; c >= 0; --c) {
3711             final StateController sc = mControllers.get(c);
3712             sc.evaluateStateLocked(job);
3713         }
3714         return job.isReady();
3715     }
3716 
3717     /**
3718      * Returns true if non-job constraint components are in place -- if job.isReady() returns true
3719      * and this method returns true, then the job is ready to be executed.
3720      */
3721     public boolean areComponentsInPlaceLocked(JobStatus job) {
3722         // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
3723         // conditions.
3724 
3725         final boolean jobExists = mJobs.containsJob(job);
3726         final boolean userStarted = areUsersStartedLocked(job);
3727         final boolean backingUp = mBackingUpUids.get(job.getSourceUid());
3728 
3729         if (DEBUG) {
3730             Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
3731                     + " exists=" + jobExists + " userStarted=" + userStarted
3732                     + " backingUp=" + backingUp);
3733         }
3734 
3735         // These are also fairly cheap to check, though they typically will not
3736         // be conditions we fail.
3737         if (!jobExists || !userStarted || backingUp) {
3738             return false;
3739         }
3740 
3741         final JobRestriction restriction = checkIfRestricted(job);
3742         if (restriction != null) {
3743             if (DEBUG) {
3744                 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
3745                         + " restricted due to " + restriction.getInternalReason());
3746             }
3747             return false;
3748         }
3749 
3750         // Job pending/active doesn't affect the readiness of a job.
3751 
3752         // The expensive check: validate that the defined package+service is
3753         // still present & viable.
3754         return isComponentUsable(job);
3755     }
3756 
3757     /** Returns the minimum amount of time we should let this job run before timing out. */
3758     public long getMinJobExecutionGuaranteeMs(JobStatus job) {
3759         synchronized (mLock) {
3760             if (job.shouldTreatAsUserInitiatedJob()
3761                     && checkRunUserInitiatedJobsPermission(
3762                             job.getSourceUid(), job.getSourcePackageName())) {
3763                 // The calling package is the one doing the work, so use it in the
3764                 // timeout quota checks.
3765                 final boolean isWithinTimeoutQuota = mQuotaTracker.isWithinQuota(
3766                         job.getTimeoutBlameUserId(), job.getTimeoutBlamePackageName(),
3767                         QUOTA_TRACKER_TIMEOUT_UIJ_TAG);
3768                 final long upperLimitMs = isWithinTimeoutQuota
3769                         ? mConstants.RUNTIME_UI_LIMIT_MS
3770                         : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
3771                 if (job.getJob().getRequiredNetwork() != null) {
3772                     // User-initiated data transfers.
3773                     if (mConstants.RUNTIME_USE_DATA_ESTIMATES_FOR_LIMITS) {
3774                         final long estimatedTransferTimeMs =
3775                                 mConnectivityController.getEstimatedTransferTimeMs(job);
3776                         if (estimatedTransferTimeMs == ConnectivityController.UNKNOWN_TIME) {
3777                             return Math.min(upperLimitMs,
3778                                     mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS);
3779                         }
3780                         // Try to give the job at least as much time as we think the transfer
3781                         // will take, but cap it at the maximum limit.
3782                         final long factoredTransferTimeMs = (long) (estimatedTransferTimeMs
3783                                 * mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_BUFFER_FACTOR);
3784                         return Math.min(upperLimitMs,
3785                                 Math.max(factoredTransferTimeMs,
3786                                         mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS));
3787                     }
3788                     return Math.min(upperLimitMs,
3789                             Math.max(mConstants.RUNTIME_MIN_UI_GUARANTEE_MS,
3790                                     mConstants.RUNTIME_MIN_UI_DATA_TRANSFER_GUARANTEE_MS));
3791                 }
3792                 return Math.min(upperLimitMs, mConstants.RUNTIME_MIN_UI_GUARANTEE_MS);
3793             } else if (job.shouldTreatAsExpeditedJob()) {
3794                 // Don't guarantee RESTRICTED jobs more than 5 minutes.
3795                 return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
3796                         ? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
3797                         : Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
3798             } else {
3799                 return mConstants.RUNTIME_MIN_GUARANTEE_MS;
3800             }
3801         }
3802     }
3803 
3804     /** Returns the maximum amount of time this job could run for. */
3805     public long getMaxJobExecutionTimeMs(JobStatus job) {
3806         synchronized (mLock) {
3807             if (job.shouldTreatAsUserInitiatedJob()
3808                     && checkRunUserInitiatedJobsPermission(
3809                             job.getSourceUid(), job.getSourcePackageName())
3810                     && mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(),
3811                             job.getTimeoutBlamePackageName(),
3812                             QUOTA_TRACKER_TIMEOUT_UIJ_TAG)) {
3813                 return mConstants.RUNTIME_UI_LIMIT_MS;
3814             }
3815             if (job.shouldTreatAsUserInitiatedJob()) {
3816                 return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
3817             }
3818             // Only let the app use the higher runtime if it hasn't repeatedly timed out.
3819             final String timeoutTag = job.shouldTreatAsExpeditedJob()
3820                     ? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG;
3821             final long upperLimitMs =
3822                     mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(),
3823                             job.getTimeoutBlamePackageName(), timeoutTag)
3824                             ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS
3825                             : mConstants.RUNTIME_MIN_GUARANTEE_MS;
3826             return Math.min(upperLimitMs,
3827                     mConstants.USE_TARE_POLICY
3828                             ? mTareController.getMaxJobExecutionTimeMsLocked(job)
3829                             : mQuotaController.getMaxJobExecutionTimeMsLocked(job));
3830         }
3831     }
3832 
3833     /**
3834      * Reconcile jobs in the pending queue against available execution contexts.
3835      * A controller can force a job into the pending queue even if it's already running, but
3836      * here is where we decide whether to actually execute it.
3837      */
3838     void maybeRunPendingJobsLocked() {
3839         if (DEBUG) {
3840             Slog.d(TAG, "pending queue: " + mPendingJobQueue.size() + " jobs.");
3841         }
3842         mConcurrencyManager.assignJobsToContextsLocked();
3843         reportActiveLocked();
3844     }
3845 
3846     private int adjustJobBias(int curBias, JobStatus job) {
3847         if (curBias < JobInfo.BIAS_TOP_APP) {
3848             float factor = mJobPackageTracker.getLoadFactor(job);
3849             if (factor >= mConstants.HEAVY_USE_FACTOR) {
3850                 curBias += JobInfo.BIAS_ADJ_ALWAYS_RUNNING;
3851             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
3852                 curBias += JobInfo.BIAS_ADJ_OFTEN_RUNNING;
3853             }
3854         }
3855         return curBias;
3856     }
3857 
3858     int evaluateJobBiasLocked(JobStatus job) {
3859         int bias = job.getBias();
3860         if (bias >= JobInfo.BIAS_BOUND_FOREGROUND_SERVICE) {
3861             return adjustJobBias(bias, job);
3862         }
3863         int override = mUidBiasOverride.get(job.getSourceUid(), 0);
3864         if (override != 0) {
3865             return adjustJobBias(override, job);
3866         }
3867         return adjustJobBias(bias, job);
3868     }
3869 
3870     void informObserversOfUserVisibleJobChange(JobServiceContext context, JobStatus jobStatus,
3871             boolean isRunning) {
3872         SomeArgs args = SomeArgs.obtain();
3873         args.arg1 = context;
3874         args.arg2 = jobStatus;
3875         args.argi1 = isRunning ? 1 : 0;
3876         mHandler.obtainMessage(MSG_INFORM_OBSERVERS_OF_USER_VISIBLE_JOB_CHANGE, args)
3877                 .sendToTarget();
3878     }
3879 
3880     private final class BatteryStateTracker extends BroadcastReceiver {
3881         /**
3882          * Track whether we're "charging", where charging means that we're ready to commit to
3883          * doing work.
3884          */
3885         private boolean mCharging;
3886         /** Keep track of whether the battery is charged enough that we want to do work. */
3887         private boolean mBatteryNotLow;
3888         /** Sequence number of last broadcast. */
3889         private int mLastBatterySeq = -1;
3890 
3891         private BroadcastReceiver mMonitor;
3892 
3893         BatteryStateTracker() {
3894         }
3895 
3896         public void startTracking() {
3897             IntentFilter filter = new IntentFilter();
3898 
3899             // Battery health.
3900             filter.addAction(Intent.ACTION_BATTERY_LOW);
3901             filter.addAction(Intent.ACTION_BATTERY_OKAY);
3902             // Charging/not charging.
3903             filter.addAction(BatteryManager.ACTION_CHARGING);
3904             filter.addAction(BatteryManager.ACTION_DISCHARGING);
3905             getTestableContext().registerReceiver(this, filter);
3906 
3907             // Initialise tracker state.
3908             BatteryManagerInternal batteryManagerInternal =
3909                     LocalServices.getService(BatteryManagerInternal.class);
3910             mBatteryNotLow = !batteryManagerInternal.getBatteryLevelLow();
3911             mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
3912         }
3913 
3914         public void setMonitorBatteryLocked(boolean enabled) {
3915             if (enabled) {
3916                 if (mMonitor == null) {
3917                     mMonitor = new BroadcastReceiver() {
3918                         @Override
3919                         public void onReceive(Context context, Intent intent) {
3920                             onReceiveInternal(intent);
3921                         }
3922                     };
3923                     IntentFilter filter = new IntentFilter();
3924                     filter.addAction(Intent.ACTION_BATTERY_CHANGED);
3925                     getTestableContext().registerReceiver(mMonitor, filter);
3926                 }
3927             } else if (mMonitor != null) {
3928                 getTestableContext().unregisterReceiver(mMonitor);
3929                 mMonitor = null;
3930             }
3931         }
3932 
3933         public boolean isCharging() {
3934             return mCharging;
3935         }
3936 
3937         public boolean isBatteryNotLow() {
3938             return mBatteryNotLow;
3939         }
3940 
3941         public boolean isMonitoring() {
3942             return mMonitor != null;
3943         }
3944 
3945         public int getSeq() {
3946             return mLastBatterySeq;
3947         }
3948 
3949         @Override
3950         public void onReceive(Context context, Intent intent) {
3951             onReceiveInternal(intent);
3952         }
3953 
3954         @VisibleForTesting
3955         public void onReceiveInternal(Intent intent) {
3956             synchronized (mLock) {
3957                 final String action = intent.getAction();
3958                 boolean changed = false;
3959                 if (Intent.ACTION_BATTERY_LOW.equals(action)) {
3960                     if (DEBUG) {
3961                         Slog.d(TAG, "Battery life too low @ " + sElapsedRealtimeClock.millis());
3962                     }
3963                     if (mBatteryNotLow) {
3964                         mBatteryNotLow = false;
3965                         changed = true;
3966                     }
3967                 } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
3968                     if (DEBUG) {
3969                         Slog.d(TAG, "Battery high enough @ " + sElapsedRealtimeClock.millis());
3970                     }
3971                     if (!mBatteryNotLow) {
3972                         mBatteryNotLow = true;
3973                         changed = true;
3974                     }
3975                 } else if (BatteryManager.ACTION_CHARGING.equals(action)) {
3976                     if (DEBUG) {
3977                         Slog.d(TAG, "Battery charging @ " + sElapsedRealtimeClock.millis());
3978                     }
3979                     if (!mCharging) {
3980                         mCharging = true;
3981                         changed = true;
3982                     }
3983                 } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
3984                     if (DEBUG) {
3985                         Slog.d(TAG, "Battery discharging @ " + sElapsedRealtimeClock.millis());
3986                     }
3987                     if (mCharging) {
3988                         mCharging = false;
3989                         changed = true;
3990                     }
3991                 }
3992                 mLastBatterySeq =
3993                         intent.getIntExtra(BatteryManager.EXTRA_SEQUENCE, mLastBatterySeq);
3994                 if (changed) {
3995                     for (int c = mControllers.size() - 1; c >= 0; --c) {
3996                         mControllers.get(c).onBatteryStateChangedLocked();
3997                     }
3998                 }
3999             }
4000         }
4001     }
4002 
4003     final class LocalService implements JobSchedulerInternal {
4004 
4005         @Override
4006         public List<JobInfo> getSystemScheduledOwnJobs(@Nullable String namespace) {
4007             synchronized (mLock) {
4008                 final List<JobInfo> ownJobs = new ArrayList<>();
4009                 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
4010                     if (job.getSourceUid() == Process.SYSTEM_UID
4011                             && Objects.equals(job.getNamespace(), namespace)
4012                             && "android".equals(job.getSourcePackageName())) {
4013                         ownJobs.add(job.getJob());
4014                     }
4015                 });
4016                 return ownJobs;
4017             }
4018         }
4019 
4020         @Override
4021         public void cancelJobsForUid(int uid, boolean includeProxiedJobs,
4022                 @JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
4023             JobSchedulerService.this.cancelJobsForUid(uid,
4024                     includeProxiedJobs, reason, internalReasonCode, debugReason);
4025         }
4026 
4027         @Override
4028         public void addBackingUpUid(int uid) {
4029             synchronized (mLock) {
4030                 // No need to actually do anything here, since for a full backup the
4031                 // activity manager will kill the process which will kill the job (and
4032                 // cause it to restart, but now it can't run).
4033                 mBackingUpUids.put(uid, true);
4034             }
4035         }
4036 
4037         @Override
4038         public void removeBackingUpUid(int uid) {
4039             synchronized (mLock) {
4040                 mBackingUpUids.delete(uid);
4041                 // If there are any jobs for this uid, we need to rebuild the pending list
4042                 // in case they are now ready to run.
4043                 if (mJobs.countJobsForUid(uid) > 0) {
4044                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
4045                 }
4046             }
4047         }
4048 
4049         @Override
4050         public void clearAllBackingUpUids() {
4051             synchronized (mLock) {
4052                 if (mBackingUpUids.size() > 0) {
4053                     mBackingUpUids.clear();
4054                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
4055                 }
4056             }
4057         }
4058 
4059         @Override
4060         public String getCloudMediaProviderPackage(int userId) {
4061             return mCloudMediaProviderPackages.get(userId);
4062         }
4063 
4064         @Override
4065         public void reportAppUsage(String packageName, int userId) {
4066             JobSchedulerService.this.reportAppUsage(packageName, userId);
4067         }
4068 
4069         @Override
4070         public boolean isAppConsideredBuggy(int callingUserId, @NonNull String callingPackageName,
4071                 int timeoutBlameUserId, @NonNull String timeoutBlamePackageName) {
4072             return !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName,
4073                             QUOTA_TRACKER_ANR_TAG)
4074                     || !mQuotaTracker.isWithinQuota(callingUserId, callingPackageName,
4075                             QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)
4076                     || !mQuotaTracker.isWithinQuota(timeoutBlameUserId, timeoutBlamePackageName,
4077                             QUOTA_TRACKER_TIMEOUT_TOTAL_TAG);
4078         }
4079 
4080         @Override
4081         public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId,
4082                 int userId, @NonNull String packageName) {
4083             if (packageName == null) {
4084                 return false;
4085             }
4086             return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs(
4087                     notificationId, userId, packageName);
4088         }
4089 
4090         @Override
4091         public boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
4092                 @NonNull String notificationChannel, int userId, @NonNull String packageName) {
4093             if (packageName == null || notificationChannel == null) {
4094                 return false;
4095             }
4096             return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
4097                     notificationChannel, userId, packageName);
4098         }
4099 
4100         @Override
4101         public JobStorePersistStats getPersistStats() {
4102             synchronized (mLock) {
4103                 return new JobStorePersistStats(mJobs.getPersistStats());
4104             }
4105         }
4106     }
4107 
4108     /**
4109      * Tracking of app assignments to standby buckets
4110      */
4111     final class StandbyTracker extends AppIdleStateChangeListener {
4112 
4113         // AppIdleStateChangeListener interface for live updates
4114 
4115         @Override
4116         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
4117                 boolean idle, int bucket, int reason) {
4118             // QuotaController handles this now.
4119         }
4120 
4121         @Override
4122         public void onUserInteractionStarted(String packageName, int userId) {
4123             final int uid = mLocalPM.getPackageUid(packageName,
4124                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
4125             if (uid < 0) {
4126                 // Quietly ignore; the case is already logged elsewhere
4127                 return;
4128             }
4129 
4130             long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
4131             if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
4132                 // Too long ago, not worth logging
4133                 sinceLast = 0L;
4134             }
4135             final DeferredJobCounter counter = new DeferredJobCounter();
4136             synchronized (mLock) {
4137                 mJobs.forEachJobForSourceUid(uid, counter);
4138             }
4139             if (counter.numDeferred() > 0 || sinceLast > 0) {
4140                 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
4141                         (BatteryStatsInternal.class);
4142                 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
4143                 FrameworkStatsLog.write_non_chained(
4144                         FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
4145                         counter.numDeferred(), sinceLast);
4146             }
4147         }
4148     }
4149 
4150     static class DeferredJobCounter implements Consumer<JobStatus> {
4151         private int mDeferred = 0;
4152 
4153         public int numDeferred() {
4154             return mDeferred;
4155         }
4156 
4157         @Override
4158         public void accept(JobStatus job) {
4159             if (job.getWhenStandbyDeferred() > 0) {
4160                 mDeferred++;
4161             }
4162         }
4163     }
4164 
4165     public static int standbyBucketToBucketIndex(int bucket) {
4166         // Normalize AppStandby constants to indices into our bookkeeping
4167         if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
4168             return NEVER_INDEX;
4169         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
4170             return RESTRICTED_INDEX;
4171         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
4172             return RARE_INDEX;
4173         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
4174             return FREQUENT_INDEX;
4175         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
4176             return WORKING_INDEX;
4177         } else if (bucket > UsageStatsManager.STANDBY_BUCKET_EXEMPTED) {
4178             return ACTIVE_INDEX;
4179         } else {
4180             return EXEMPTED_INDEX;
4181         }
4182     }
4183 
4184     // Static to support external callers
4185     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
4186         UsageStatsManagerInternal usageStats = LocalServices.getService(
4187                 UsageStatsManagerInternal.class);
4188         int bucket = usageStats != null
4189                 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
4190                 : 0;
4191 
4192         bucket = standbyBucketToBucketIndex(bucket);
4193 
4194         if (DEBUG_STANDBY) {
4195             Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
4196         }
4197         return bucket;
4198     }
4199 
4200     static int safelyScaleBytesToKBForHistogram(long bytes) {
4201         long kilobytes = bytes / 1000;
4202         // Anything over Integer.MAX_VALUE or under Integer.MIN_VALUE isn't expected and will
4203         // be put into the overflow buckets.
4204         if (kilobytes > Integer.MAX_VALUE) {
4205             return Integer.MAX_VALUE;
4206         } else if (kilobytes < Integer.MIN_VALUE) {
4207             return Integer.MIN_VALUE;
4208         }
4209         return (int) kilobytes;
4210     }
4211 
4212     private class CloudProviderChangeListener implements
4213             StorageManagerInternal.CloudProviderChangeListener {
4214 
4215         @Override
4216         public void onCloudProviderChanged(int userId, @Nullable String authority) {
4217             final PackageManager pm = getContext()
4218                     .createContextAsUser(UserHandle.of(userId), 0)
4219                     .getPackageManager();
4220             final ProviderInfo pi = pm.resolveContentProvider(
4221                     authority, PackageManager.ComponentInfoFlags.of(0));
4222             final String newPkg = (pi == null) ? null : pi.packageName;
4223             synchronized (mLock) {
4224                 final String oldPkg = mCloudMediaProviderPackages.get(userId);
4225                 if (!Objects.equals(oldPkg, newPkg)) {
4226                     if (DEBUG) {
4227                         Slog.d(TAG, "Cloud provider of user " + userId + " changed from " + oldPkg
4228                                 + " to " + newPkg);
4229                     }
4230                     mCloudMediaProviderPackages.put(userId, newPkg);
4231                     SomeArgs args = SomeArgs.obtain();
4232                     args.argi1 = userId;
4233                     args.arg1 = oldPkg;
4234                     args.arg2 = newPkg;
4235                     mHandler.obtainMessage(MSG_CHECK_MEDIA_EXEMPTION, args).sendToTarget();
4236                 }
4237             }
4238         }
4239     }
4240 
4241     /**
4242      * Returns whether the app has the permission granted.
4243      * This currently only works for normal permissions and <b>DOES NOT</b> work for runtime
4244      * permissions.
4245      * TODO: handle runtime permissions
4246      */
4247     private boolean hasPermission(int uid, int pid, @NonNull String permission) {
4248         synchronized (mPermissionCache) {
4249             SparseArrayMap<String, Boolean> pidPermissions = mPermissionCache.get(uid);
4250             if (pidPermissions == null) {
4251                 pidPermissions = new SparseArrayMap<>();
4252                 mPermissionCache.put(uid, pidPermissions);
4253             }
4254             final Boolean cached = pidPermissions.get(pid, permission);
4255             if (cached != null) {
4256                 return cached;
4257             }
4258 
4259             final int result = getContext().checkPermission(permission, pid, uid);
4260             final boolean permissionGranted = (result == PackageManager.PERMISSION_GRANTED);
4261             pidPermissions.add(pid, permission, permissionGranted);
4262             return permissionGranted;
4263         }
4264     }
4265 
4266     /**
4267      * Binder stub trampoline implementation
4268      */
4269     final class JobSchedulerStub extends IJobScheduler.Stub {
4270         // Enforce that only the app itself (or shared uid participant) can schedule a
4271         // job that runs one of the app's services, as well as verifying that the
4272         // named service properly requires the BIND_JOB_SERVICE permission
4273         // TODO(141645789): merge enforceValidJobRequest() with validateJob()
4274         private void enforceValidJobRequest(int uid, int pid, JobInfo job) {
4275             final PackageManager pm = getContext()
4276                     .createContextAsUser(UserHandle.getUserHandleForUid(uid), 0)
4277                     .getPackageManager();
4278             final ComponentName service = job.getService();
4279             try {
4280                 ServiceInfo si = pm.getServiceInfo(service,
4281                         PackageManager.MATCH_DIRECT_BOOT_AWARE
4282                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
4283                 if (si == null) {
4284                     throw new IllegalArgumentException("No such service " + service);
4285                 }
4286                 if (si.applicationInfo.uid != uid) {
4287                     throw new IllegalArgumentException("uid " + uid +
4288                             " cannot schedule job in " + service.getPackageName());
4289                 }
4290                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
4291                     throw new IllegalArgumentException("Scheduled service " + service
4292                             + " does not require android.permission.BIND_JOB_SERVICE permission");
4293                 }
4294             } catch (NameNotFoundException e) {
4295                 throw new IllegalArgumentException(
4296                         "Tried to schedule job for non-existent component: " + service);
4297             }
4298             // If we get this far we're good to go; all we need to do now is check
4299             // whether the app is allowed to persist its scheduled work.
4300             if (job.isPersisted() && !canPersistJobs(pid, uid)) {
4301                 throw new IllegalArgumentException("Requested job cannot be persisted without"
4302                         + " holding android.permission.RECEIVE_BOOT_COMPLETED permission");
4303             }
4304             if (job.getRequiredNetwork() != null
4305                     && CompatChanges.isChangeEnabled(
4306                             REQUIRE_NETWORK_PERMISSIONS_FOR_CONNECTIVITY_JOBS, uid)) {
4307                 if (!hasPermission(uid, pid, Manifest.permission.ACCESS_NETWORK_STATE)) {
4308                     throw new SecurityException(Manifest.permission.ACCESS_NETWORK_STATE
4309                             + " required for jobs with a connectivity constraint");
4310                 }
4311             }
4312         }
4313 
4314         private boolean canPersistJobs(int pid, int uid) {
4315             // Persisting jobs is tantamount to running at boot, so we permit
4316             // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
4317             // permission
4318             return hasPermission(uid, pid, Manifest.permission.RECEIVE_BOOT_COMPLETED);
4319         }
4320 
4321         private int validateJob(@NonNull JobInfo job, int callingUid, int callingPid,
4322                 int sourceUserId,
4323                 @Nullable String sourcePkgName, @Nullable JobWorkItem jobWorkItem) {
4324             final boolean rejectNegativeNetworkEstimates = CompatChanges.isChangeEnabled(
4325                             JobInfo.REJECT_NEGATIVE_NETWORK_ESTIMATES, callingUid);
4326             job.enforceValidity(
4327                     CompatChanges.isChangeEnabled(
4328                             JobInfo.DISALLOW_DEADLINES_FOR_PREFETCH_JOBS, callingUid),
4329                     rejectNegativeNetworkEstimates);
4330             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
4331                 getContext().enforceCallingOrSelfPermission(
4332                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
4333             }
4334             if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
4335                 if (callingUid != Process.SYSTEM_UID) {
4336                     throw new SecurityException("Job has invalid flags");
4337                 }
4338                 if (job.isPeriodic()) {
4339                     Slog.wtf(TAG, "Periodic jobs mustn't have"
4340                             + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
4341                 }
4342             }
4343             if (job.isUserInitiated()) {
4344                 int sourceUid = -1;
4345                 if (sourceUserId != -1 && sourcePkgName != null) {
4346                     try {
4347                         sourceUid = AppGlobals.getPackageManager().getPackageUid(
4348                                 sourcePkgName, 0, sourceUserId);
4349                     } catch (RemoteException ex) {
4350                         // Can't happen, PackageManager runs in the same process.
4351                     }
4352                 }
4353                 // We aim to check the permission of both the source and calling app so that apps
4354                 // don't attempt to bypass the permission by using other apps to do the work.
4355                 boolean isInStateToScheduleUiJobSource = false;
4356                 final String callingPkgName = job.getService().getPackageName();
4357                 if (sourceUid != -1) {
4358                     // Check the permission of the source app.
4359                     final int sourceResult =
4360                             validateRunUserInitiatedJobsPermission(sourceUid, sourcePkgName);
4361                     if (sourceResult != JobScheduler.RESULT_SUCCESS) {
4362                         return sourceResult;
4363                     }
4364                     final int sourcePid =
4365                             callingUid == sourceUid && callingPkgName.equals(sourcePkgName)
4366                                     ? callingPid : -1;
4367                     isInStateToScheduleUiJobSource = isInStateToScheduleUserInitiatedJobs(
4368                             sourceUid, sourcePid, sourcePkgName);
4369                 }
4370                 boolean isInStateToScheduleUiJobCalling = false;
4371                 if (callingUid != sourceUid || !callingPkgName.equals(sourcePkgName)) {
4372                     // Source app is different from calling app. Make sure the calling app also has
4373                     // the permission.
4374                     final int callingResult =
4375                             validateRunUserInitiatedJobsPermission(callingUid, callingPkgName);
4376                     if (callingResult != JobScheduler.RESULT_SUCCESS) {
4377                         return callingResult;
4378                     }
4379                     // Avoid rechecking the state if the source app is able to schedule the job.
4380                     if (!isInStateToScheduleUiJobSource) {
4381                         isInStateToScheduleUiJobCalling = isInStateToScheduleUserInitiatedJobs(
4382                                 callingUid, callingPid, callingPkgName);
4383                     }
4384                 }
4385 
4386                 if (!isInStateToScheduleUiJobSource && !isInStateToScheduleUiJobCalling) {
4387                     Slog.e(TAG, "Uid(s) " + sourceUid + "/" + callingUid
4388                             + " not in a state to schedule user-initiated jobs");
4389                     Counter.logIncrementWithUid(
4390                             "job_scheduler.value_cntr_w_uid_schedule_failure_uij_invalid_state",
4391                             callingUid);
4392                     return JobScheduler.RESULT_FAILURE;
4393                 }
4394             }
4395             if (jobWorkItem != null) {
4396                 jobWorkItem.enforceValidity(rejectNegativeNetworkEstimates);
4397                 if (jobWorkItem.getEstimatedNetworkDownloadBytes() != JobInfo.NETWORK_BYTES_UNKNOWN
4398                         || jobWorkItem.getEstimatedNetworkUploadBytes()
4399                         != JobInfo.NETWORK_BYTES_UNKNOWN
4400                         || jobWorkItem.getMinimumNetworkChunkBytes()
4401                         != JobInfo.NETWORK_BYTES_UNKNOWN) {
4402                     if (job.getRequiredNetwork() == null) {
4403                         final String errorMsg = "JobWorkItem implies network usage"
4404                                 + " but job doesn't specify a network constraint";
4405                         if (CompatChanges.isChangeEnabled(
4406                                 REQUIRE_NETWORK_CONSTRAINT_FOR_NETWORK_JOB_WORK_ITEMS,
4407                                 callingUid)) {
4408                             throw new IllegalArgumentException(errorMsg);
4409                         } else {
4410                             Slog.e(TAG, errorMsg);
4411                         }
4412                     }
4413                 }
4414                 if (job.isPersisted()) {
4415                     // Intent.saveToXml() doesn't persist everything, so just reject all
4416                     // JobWorkItems with Intents to be safe/predictable.
4417                     if (jobWorkItem.getIntent() != null) {
4418                         throw new IllegalArgumentException(
4419                                 "Cannot persist JobWorkItems with Intents");
4420                     }
4421                 }
4422             }
4423             return JobScheduler.RESULT_SUCCESS;
4424         }
4425 
4426         /** Returns a sanitized namespace if valid, or throws an exception if not. */
4427         private String validateNamespace(@Nullable String namespace) {
4428             namespace = JobScheduler.sanitizeNamespace(namespace);
4429             if (namespace != null) {
4430                 if (namespace.isEmpty()) {
4431                     throw new IllegalArgumentException("namespace cannot be empty");
4432                 }
4433                 if (namespace.length() > 1000) {
4434                     throw new IllegalArgumentException(
4435                             "namespace cannot be more than 1000 characters");
4436                 }
4437                 namespace = namespace.intern();
4438             }
4439             return namespace;
4440         }
4441 
4442         private int validateRunUserInitiatedJobsPermission(int uid, String packageName) {
4443             final int state = getRunUserInitiatedJobsPermissionState(uid, packageName);
4444             if (state == PermissionChecker.PERMISSION_HARD_DENIED) {
4445                 Counter.logIncrementWithUid(
4446                         "job_scheduler.value_cntr_w_uid_schedule_failure_uij_no_permission", uid);
4447                 throw new SecurityException(android.Manifest.permission.RUN_USER_INITIATED_JOBS
4448                         + " required to schedule user-initiated jobs.");
4449             }
4450             if (state == PermissionChecker.PERMISSION_SOFT_DENIED) {
4451                 Counter.logIncrementWithUid(
4452                         "job_scheduler.value_cntr_w_uid_schedule_failure_uij_no_permission", uid);
4453                 return JobScheduler.RESULT_FAILURE;
4454             }
4455             return JobScheduler.RESULT_SUCCESS;
4456         }
4457 
4458         private boolean isInStateToScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
4459             final int procState = mActivityManagerInternal.getUidProcessState(uid);
4460             if (DEBUG) {
4461                 Slog.d(TAG, "Uid " + uid + " proc state="
4462                         + ActivityManager.procStateToString(procState));
4463             }
4464             if (procState == ActivityManager.PROCESS_STATE_TOP) {
4465                 return true;
4466             }
4467             final boolean canScheduleUiJobsInBg =
4468                     mActivityManagerInternal.canScheduleUserInitiatedJobs(uid, pid, pkgName);
4469             if (DEBUG) {
4470                 Slog.d(TAG, "Uid " + uid
4471                         + " AM.canScheduleUserInitiatedJobs= " + canScheduleUiJobsInBg);
4472             }
4473             return canScheduleUiJobsInBg;
4474         }
4475 
4476         // IJobScheduler implementation
4477         @Override
4478         public int schedule(String namespace, JobInfo job) throws RemoteException {
4479             if (DEBUG) {
4480                 Slog.d(TAG, "Scheduling job: " + job.toString());
4481             }
4482             final int pid = Binder.getCallingPid();
4483             final int uid = Binder.getCallingUid();
4484             final int userId = UserHandle.getUserId(uid);
4485 
4486             enforceValidJobRequest(uid, pid, job);
4487 
4488             final int result = validateJob(job, uid, pid, -1, null, null);
4489             if (result != JobScheduler.RESULT_SUCCESS) {
4490                 return result;
4491             }
4492 
4493             namespace = validateNamespace(namespace);
4494 
4495             final long ident = Binder.clearCallingIdentity();
4496             try {
4497                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
4498                         namespace, null);
4499             } finally {
4500                 Binder.restoreCallingIdentity(ident);
4501             }
4502         }
4503 
4504         // IJobScheduler implementation
4505         @Override
4506         public int enqueue(String namespace, JobInfo job, JobWorkItem work) throws RemoteException {
4507             if (DEBUG) {
4508                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
4509             }
4510             final int uid = Binder.getCallingUid();
4511             final int pid = Binder.getCallingPid();
4512             final int userId = UserHandle.getUserId(uid);
4513 
4514             enforceValidJobRequest(uid, pid, job);
4515             if (work == null) {
4516                 throw new NullPointerException("work is null");
4517             }
4518 
4519             final int result = validateJob(job, uid, pid, -1, null, work);
4520             if (result != JobScheduler.RESULT_SUCCESS) {
4521                 return result;
4522             }
4523 
4524             namespace = validateNamespace(namespace);
4525 
4526             final long ident = Binder.clearCallingIdentity();
4527             try {
4528                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
4529                         namespace, null);
4530             } finally {
4531                 Binder.restoreCallingIdentity(ident);
4532             }
4533         }
4534 
4535         @Override
4536         public int scheduleAsPackage(String namespace, JobInfo job, String packageName, int userId,
4537                 String tag) throws RemoteException {
4538             final int callerUid = Binder.getCallingUid();
4539             final int callerPid = Binder.getCallingPid();
4540             if (DEBUG) {
4541                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
4542                         + " on behalf of " + packageName + "/");
4543             }
4544 
4545             if (packageName == null) {
4546                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
4547             }
4548 
4549             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
4550                     android.Manifest.permission.UPDATE_DEVICE_STATS);
4551             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
4552                 throw new SecurityException("Caller uid " + callerUid
4553                         + " not permitted to schedule jobs for other apps");
4554             }
4555 
4556             enforceValidJobRequest(callerUid, callerPid, job);
4557 
4558             int result = validateJob(job, callerUid, callerPid, userId, packageName, null);
4559             if (result != JobScheduler.RESULT_SUCCESS) {
4560                 return result;
4561             }
4562 
4563             namespace = validateNamespace(namespace);
4564 
4565             final long ident = Binder.clearCallingIdentity();
4566             try {
4567                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
4568                         packageName, userId, namespace, tag);
4569             } finally {
4570                 Binder.restoreCallingIdentity(ident);
4571             }
4572         }
4573 
4574         @Override
4575         public Map<String, ParceledListSlice<JobInfo>> getAllPendingJobs() throws RemoteException {
4576             final int uid = Binder.getCallingUid();
4577 
4578             final long ident = Binder.clearCallingIdentity();
4579             try {
4580                 final ArrayMap<String, List<JobInfo>> jobs =
4581                         JobSchedulerService.this.getPendingJobs(uid);
4582                 final ArrayMap<String, ParceledListSlice<JobInfo>> outMap = new ArrayMap<>();
4583                 for (int i = 0; i < jobs.size(); ++i) {
4584                     outMap.put(jobs.keyAt(i), new ParceledListSlice<>(jobs.valueAt(i)));
4585                 }
4586                 return outMap;
4587             } finally {
4588                 Binder.restoreCallingIdentity(ident);
4589             }
4590         }
4591 
4592         @Override
4593         public ParceledListSlice<JobInfo> getAllPendingJobsInNamespace(String namespace)
4594                 throws RemoteException {
4595             final int uid = Binder.getCallingUid();
4596 
4597             final long ident = Binder.clearCallingIdentity();
4598             try {
4599                 return new ParceledListSlice<>(
4600                         JobSchedulerService.this.getPendingJobsInNamespace(uid,
4601                                 validateNamespace(namespace)));
4602             } finally {
4603                 Binder.restoreCallingIdentity(ident);
4604             }
4605         }
4606 
4607         @Override
4608         public JobInfo getPendingJob(String namespace, int jobId) throws RemoteException {
4609             final int uid = Binder.getCallingUid();
4610 
4611             final long ident = Binder.clearCallingIdentity();
4612             try {
4613                 return JobSchedulerService.this.getPendingJob(
4614                         uid, validateNamespace(namespace), jobId);
4615             } finally {
4616                 Binder.restoreCallingIdentity(ident);
4617             }
4618         }
4619 
4620         @Override
4621         public int getPendingJobReason(String namespace, int jobId) throws RemoteException {
4622             final int uid = Binder.getCallingUid();
4623 
4624             final long ident = Binder.clearCallingIdentity();
4625             try {
4626                 return JobSchedulerService.this.getPendingJobReason(
4627                         uid, validateNamespace(namespace), jobId);
4628             } finally {
4629                 Binder.restoreCallingIdentity(ident);
4630             }
4631         }
4632 
4633         @Override
4634         public void cancelAll() throws RemoteException {
4635             final int uid = Binder.getCallingUid();
4636             final long ident = Binder.clearCallingIdentity();
4637             try {
4638                 JobSchedulerService.this.cancelJobsForUid(uid,
4639                         // Documentation says only jobs scheduled BY the app will be cancelled
4640                         /* includeSourceApp */ false,
4641                         JobParameters.STOP_REASON_CANCELLED_BY_APP,
4642                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
4643                         "cancelAll() called by app, callingUid=" + uid);
4644             } finally {
4645                 Binder.restoreCallingIdentity(ident);
4646             }
4647         }
4648 
4649         @Override
4650         public void cancelAllInNamespace(String namespace) throws RemoteException {
4651             final int uid = Binder.getCallingUid();
4652             final long ident = Binder.clearCallingIdentity();
4653             try {
4654                 JobSchedulerService.this.cancelJobsForUid(uid,
4655                         // Documentation says only jobs scheduled BY the app will be cancelled
4656                         /* includeSourceApp */ false,
4657                         /* namespaceOnly */ true, validateNamespace(namespace),
4658                         JobParameters.STOP_REASON_CANCELLED_BY_APP,
4659                         JobParameters.INTERNAL_STOP_REASON_CANCELED,
4660                         "cancelAllInNamespace() called by app, callingUid=" + uid);
4661             } finally {
4662                 Binder.restoreCallingIdentity(ident);
4663             }
4664         }
4665 
4666         @Override
4667         public void cancel(String namespace, int jobId) throws RemoteException {
4668             final int uid = Binder.getCallingUid();
4669 
4670             final long ident = Binder.clearCallingIdentity();
4671             try {
4672                 JobSchedulerService.this.cancelJob(uid, validateNamespace(namespace), jobId, uid,
4673                         JobParameters.STOP_REASON_CANCELLED_BY_APP);
4674             } finally {
4675                 Binder.restoreCallingIdentity(ident);
4676             }
4677         }
4678 
4679         public boolean canRunUserInitiatedJobs(@NonNull String packageName) {
4680             final int callingUid = Binder.getCallingUid();
4681             final int userId = UserHandle.getUserId(callingUid);
4682             final int packageUid = mLocalPM.getPackageUid(packageName, 0, userId);
4683             if (callingUid != packageUid) {
4684                 throw new SecurityException("Uid " + callingUid
4685                         + " cannot query canRunUserInitiatedJobs for package " + packageName);
4686             }
4687 
4688             return checkRunUserInitiatedJobsPermission(packageUid, packageName);
4689         }
4690 
4691         public boolean hasRunUserInitiatedJobsPermission(@NonNull String packageName,
4692                 @UserIdInt int userId) {
4693             final int uid = mLocalPM.getPackageUid(packageName, 0, userId);
4694             final int callingUid = Binder.getCallingUid();
4695             if (callingUid != uid && !UserHandle.isCore(callingUid)) {
4696                 throw new SecurityException("Uid " + callingUid
4697                         + " cannot query hasRunUserInitiatedJobsPermission for package "
4698                         + packageName);
4699             }
4700 
4701             return checkRunUserInitiatedJobsPermission(uid, packageName);
4702         }
4703 
4704         /**
4705          * "dumpsys" infrastructure
4706          */
4707         @Override
4708         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4709             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
4710 
4711             int filterUid = -1;
4712             boolean proto = false;
4713             if (!ArrayUtils.isEmpty(args)) {
4714                 int opti = 0;
4715                 while (opti < args.length) {
4716                     String arg = args[opti];
4717                     if ("-h".equals(arg)) {
4718                         dumpHelp(pw);
4719                         return;
4720                     } else if ("-a".equals(arg)) {
4721                         // Ignore, we always dump all.
4722                     } else if ("--proto".equals(arg)) {
4723                         proto = true;
4724                     } else if (arg.length() > 0 && arg.charAt(0) == '-') {
4725                         pw.println("Unknown option: " + arg);
4726                         return;
4727                     } else {
4728                         break;
4729                     }
4730                     opti++;
4731                 }
4732                 if (opti < args.length) {
4733                     String pkg = args[opti];
4734                     try {
4735                         filterUid = getContext().getPackageManager().getPackageUid(pkg,
4736                                 PackageManager.MATCH_ANY_USER);
4737                     } catch (NameNotFoundException ignored) {
4738                         pw.println("Invalid package: " + pkg);
4739                         return;
4740                     }
4741                 }
4742             }
4743 
4744             final long identityToken = Binder.clearCallingIdentity();
4745             try {
4746                 if (proto) {
4747                     JobSchedulerService.this.dumpInternalProto(fd, filterUid);
4748                 } else {
4749                     JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, "  "),
4750                             filterUid);
4751                 }
4752             } finally {
4753                 Binder.restoreCallingIdentity(identityToken);
4754             }
4755         }
4756 
4757         @Override
4758         public int handleShellCommand(@NonNull ParcelFileDescriptor in,
4759                 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
4760                 @NonNull String[] args) {
4761             return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
4762                     this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
4763                     args);
4764         }
4765 
4766         /**
4767          * <b>For internal system user only!</b>
4768          * Returns a list of all currently-executing jobs.
4769          */
4770         @Override
4771         public List<JobInfo> getStartedJobs() {
4772             final int uid = Binder.getCallingUid();
4773             if (uid != Process.SYSTEM_UID) {
4774                 throw new SecurityException("getStartedJobs() is system internal use only.");
4775             }
4776 
4777             final ArrayList<JobInfo> runningJobs;
4778 
4779             synchronized (mLock) {
4780                 final ArraySet<JobStatus> runningJobStatuses =
4781                         mConcurrencyManager.getRunningJobsLocked();
4782                 runningJobs = new ArrayList<>(runningJobStatuses.size());
4783                 for (int i = runningJobStatuses.size() - 1; i >= 0; --i) {
4784                     final JobStatus job = runningJobStatuses.valueAt(i);
4785                     if (job != null) {
4786                         runningJobs.add(job.getJob());
4787                     }
4788                 }
4789             }
4790 
4791             return runningJobs;
4792         }
4793 
4794         /**
4795          * <b>For internal system user only!</b>
4796          * Returns a snapshot of the state of all jobs known to the system.
4797          *
4798          * <p class="note">This is a slow operation, so it should be called sparingly.
4799          */
4800         @Override
4801         public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
4802             final int uid = Binder.getCallingUid();
4803             if (uid != Process.SYSTEM_UID) {
4804                 throw new SecurityException("getAllJobSnapshots() is system internal use only.");
4805             }
4806             synchronized (mLock) {
4807                 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
4808                 mJobs.forEachJob((job) -> snapshots.add(
4809                         new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
4810                                 isReadyToBeExecutedLocked(job))));
4811                 return new ParceledListSlice<>(snapshots);
4812             }
4813         }
4814 
4815         @Override
4816         @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL})
4817         public void registerUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
4818             super.registerUserVisibleJobObserver_enforcePermission();
4819             if (observer == null) {
4820                 throw new NullPointerException("observer");
4821             }
4822             mUserVisibleJobObservers.register(observer);
4823             mHandler.obtainMessage(MSG_INFORM_OBSERVER_OF_ALL_USER_VISIBLE_JOBS, observer)
4824                     .sendToTarget();
4825         }
4826 
4827         @Override
4828         @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL})
4829         public void unregisterUserVisibleJobObserver(@NonNull IUserVisibleJobObserver observer) {
4830             super.unregisterUserVisibleJobObserver_enforcePermission();
4831             if (observer == null) {
4832                 throw new NullPointerException("observer");
4833             }
4834             mUserVisibleJobObservers.unregister(observer);
4835         }
4836 
4837         @Override
4838         @EnforcePermission(allOf = {MANAGE_ACTIVITY_TASKS, INTERACT_ACROSS_USERS_FULL})
4839         public void notePendingUserRequestedAppStop(@NonNull String packageName, int userId,
4840                 @Nullable String debugReason) {
4841             super.notePendingUserRequestedAppStop_enforcePermission();
4842             if (packageName == null) {
4843                 throw new NullPointerException("packageName");
4844             }
4845             notePendingUserRequestedAppStopInternal(packageName, userId, debugReason);
4846         }
4847     }
4848 
4849     // Shell command infrastructure: run the given job immediately
4850     int executeRunCommand(String pkgName, int userId, @Nullable String namespace,
4851             int jobId, boolean satisfied, boolean force) {
4852         Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + namespace + "/" + userId
4853                 + " " + jobId + " s=" + satisfied + " f=" + force);
4854 
4855         try {
4856             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
4857                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
4858             if (uid < 0) {
4859                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
4860             }
4861 
4862             synchronized (mLock) {
4863                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
4864                 if (js == null) {
4865                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
4866                 }
4867 
4868                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL
4869                         : (satisfied ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_SOFT);
4870 
4871                 // Re-evaluate constraints after the override is set in case one of the overridden
4872                 // constraints was preventing another constraint from thinking it needed to update.
4873                 for (int c = mControllers.size() - 1; c >= 0; --c) {
4874                     mControllers.get(c).reevaluateStateLocked(uid);
4875                 }
4876 
4877                 if (!js.isConstraintsSatisfied()) {
4878                     js.overrideState = JobStatus.OVERRIDE_NONE;
4879                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
4880                 }
4881 
4882                 queueReadyJobsForExecutionLocked();
4883                 maybeRunPendingJobsLocked();
4884             }
4885         } catch (RemoteException e) {
4886             // can't happen
4887         }
4888         return 0;
4889     }
4890 
4891     // Shell command infrastructure: immediately timeout currently executing jobs
4892     int executeStopCommand(PrintWriter pw, String pkgName, int userId,
4893             @Nullable String namespace, boolean hasJobId, int jobId,
4894             int stopReason, int internalStopReason) {
4895         if (DEBUG) {
4896             Slog.v(TAG, "executeStopJobCommand(): " + pkgName + "/" + userId + " " + jobId
4897                     + ": " + stopReason + "("
4898                     + JobParameters.getInternalReasonCodeDescription(internalStopReason) + ")");
4899         }
4900 
4901         synchronized (mLock) {
4902             final boolean foundSome = mConcurrencyManager.executeStopCommandLocked(pw,
4903                     pkgName, userId, namespace, hasJobId, jobId, stopReason, internalStopReason);
4904             if (!foundSome) {
4905                 pw.println("No matching executing jobs found.");
4906             }
4907         }
4908         return 0;
4909     }
4910 
4911     // Shell command infrastructure: cancel a scheduled job
4912     int executeCancelCommand(PrintWriter pw, String pkgName, int userId, @Nullable String namespace,
4913             boolean hasJobId, int jobId) {
4914         if (DEBUG) {
4915             Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
4916         }
4917 
4918         int pkgUid = -1;
4919         try {
4920             IPackageManager pm = AppGlobals.getPackageManager();
4921             pkgUid = pm.getPackageUid(pkgName, 0, userId);
4922         } catch (RemoteException e) { /* can't happen */ }
4923 
4924         if (pkgUid < 0) {
4925             pw.println("Package " + pkgName + " not found.");
4926             return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
4927         }
4928 
4929         if (!hasJobId) {
4930             pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
4931             if (!cancelJobsForUid(pkgUid,
4932                     /* includeSourceApp */ false,
4933                     JobParameters.STOP_REASON_USER,
4934                     JobParameters.INTERNAL_STOP_REASON_CANCELED,
4935                     "cancel shell command for package")) {
4936                 pw.println("No matching jobs found.");
4937             }
4938         } else {
4939             pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
4940             if (!cancelJob(pkgUid, namespace, jobId,
4941                     Process.SHELL_UID, JobParameters.STOP_REASON_USER)) {
4942                 pw.println("No matching job found.");
4943             }
4944         }
4945 
4946         return 0;
4947     }
4948 
4949     void setMonitorBattery(boolean enabled) {
4950         synchronized (mLock) {
4951             mBatteryStateTracker.setMonitorBatteryLocked(enabled);
4952         }
4953     }
4954 
4955     int getBatterySeq() {
4956         synchronized (mLock) {
4957             return mBatteryStateTracker.getSeq();
4958         }
4959     }
4960 
4961     /** Return {@code true} if the device is currently charging. */
4962     public boolean isBatteryCharging() {
4963         synchronized (mLock) {
4964             return mBatteryStateTracker.isCharging();
4965         }
4966     }
4967 
4968     /** Return {@code true} if the battery is not low. */
4969     public boolean isBatteryNotLow() {
4970         synchronized (mLock) {
4971             return mBatteryStateTracker.isBatteryNotLow();
4972         }
4973     }
4974 
4975     int getStorageSeq() {
4976         synchronized (mLock) {
4977             return mStorageController.getTracker().getSeq();
4978         }
4979     }
4980 
4981     boolean getStorageNotLow() {
4982         synchronized (mLock) {
4983             return mStorageController.getTracker().isStorageNotLow();
4984         }
4985     }
4986 
4987     int getEstimatedNetworkBytes(PrintWriter pw, String pkgName, int userId, String namespace,
4988             int jobId, int byteOption) {
4989         try {
4990             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
4991                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
4992             if (uid < 0) {
4993                 pw.print("unknown(");
4994                 pw.print(pkgName);
4995                 pw.println(")");
4996                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
4997             }
4998 
4999             synchronized (mLock) {
5000                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
5001                 if (DEBUG) {
5002                     Slog.d(TAG, "get-estimated-network-bytes " + uid + "/"
5003                             + namespace + "/" + jobId + ": " + js);
5004                 }
5005                 if (js == null) {
5006                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
5007                     pw.print("/jid"); pw.print(jobId); pw.println(")");
5008                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
5009                 }
5010 
5011                 final long downloadBytes;
5012                 final long uploadBytes;
5013                 final Pair<Long, Long> bytes = mConcurrencyManager.getEstimatedNetworkBytesLocked(
5014                         pkgName, uid, namespace, jobId);
5015                 if (bytes == null) {
5016                     downloadBytes = js.getEstimatedNetworkDownloadBytes();
5017                     uploadBytes = js.getEstimatedNetworkUploadBytes();
5018                 } else {
5019                     downloadBytes = bytes.first;
5020                     uploadBytes = bytes.second;
5021                 }
5022                 if (byteOption == JobSchedulerShellCommand.BYTE_OPTION_DOWNLOAD) {
5023                     pw.println(downloadBytes);
5024                 } else {
5025                     pw.println(uploadBytes);
5026                 }
5027                 pw.println();
5028             }
5029         } catch (RemoteException e) {
5030             // can't happen
5031         }
5032         return 0;
5033     }
5034 
5035     int getTransferredNetworkBytes(PrintWriter pw, String pkgName, int userId, String namespace,
5036             int jobId, int byteOption) {
5037         try {
5038             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
5039                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
5040             if (uid < 0) {
5041                 pw.print("unknown(");
5042                 pw.print(pkgName);
5043                 pw.println(")");
5044                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
5045             }
5046 
5047             synchronized (mLock) {
5048                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
5049                 if (DEBUG) {
5050                     Slog.d(TAG, "get-transferred-network-bytes " + uid
5051                             + namespace + "/" + "/" + jobId + ": " + js);
5052                 }
5053                 if (js == null) {
5054                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
5055                     pw.print("/jid"); pw.print(jobId); pw.println(")");
5056                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
5057                 }
5058 
5059                 final long downloadBytes;
5060                 final long uploadBytes;
5061                 final Pair<Long, Long> bytes = mConcurrencyManager.getTransferredNetworkBytesLocked(
5062                         pkgName, uid, namespace, jobId);
5063                 if (bytes == null) {
5064                     downloadBytes = 0;
5065                     uploadBytes = 0;
5066                 } else {
5067                     downloadBytes = bytes.first;
5068                     uploadBytes = bytes.second;
5069                 }
5070                 if (byteOption == JobSchedulerShellCommand.BYTE_OPTION_DOWNLOAD) {
5071                     pw.println(downloadBytes);
5072                 } else {
5073                     pw.println(uploadBytes);
5074                 }
5075                 pw.println();
5076             }
5077         } catch (RemoteException e) {
5078             // can't happen
5079         }
5080         return 0;
5081     }
5082 
5083     /** Returns true if both the appop and permission are granted. */
5084     private boolean checkRunUserInitiatedJobsPermission(int packageUid, String packageName) {
5085         return getRunUserInitiatedJobsPermissionState(packageUid, packageName)
5086                 == PermissionChecker.PERMISSION_GRANTED;
5087     }
5088 
5089     private int getRunUserInitiatedJobsPermissionState(int packageUid, String packageName) {
5090         return PermissionChecker.checkPermissionForPreflight(getTestableContext(),
5091                 android.Manifest.permission.RUN_USER_INITIATED_JOBS, PermissionChecker.PID_UNKNOWN,
5092                 packageUid, packageName);
5093     }
5094 
5095     @VisibleForTesting
5096     protected ConnectivityController getConnectivityController() {
5097         return mConnectivityController;
5098     }
5099 
5100     @VisibleForTesting
5101     protected QuotaController getQuotaController() {
5102         return mQuotaController;
5103     }
5104 
5105     @VisibleForTesting
5106     protected TareController getTareController() {
5107         return mTareController;
5108     }
5109 
5110     // Shell command infrastructure
5111     int getJobState(PrintWriter pw, String pkgName, int userId, @Nullable String namespace,
5112             int jobId) {
5113         try {
5114             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
5115                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
5116             if (uid < 0) {
5117                 pw.print("unknown(");
5118                 pw.print(pkgName);
5119                 pw.println(")");
5120                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
5121             }
5122 
5123             synchronized (mLock) {
5124                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, namespace, jobId);
5125                 if (DEBUG) {
5126                     Slog.d(TAG,
5127                             "get-job-state " + namespace + "/" + uid + "/" + jobId + ": " + js);
5128                 }
5129                 if (js == null) {
5130                     pw.print("unknown(");
5131                     UserHandle.formatUid(pw, uid);
5132                     pw.print("/jid");
5133                     pw.print(jobId);
5134                     pw.println(")");
5135                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
5136                 }
5137 
5138                 boolean printed = false;
5139                 if (mPendingJobQueue.contains(js)) {
5140                     pw.print("pending");
5141                     printed = true;
5142                 }
5143                 if (mConcurrencyManager.isJobRunningLocked(js)) {
5144                     if (printed) {
5145                         pw.print(" ");
5146                     }
5147                     printed = true;
5148                     pw.println("active");
5149                 }
5150                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
5151                     if (printed) {
5152                         pw.print(" ");
5153                     }
5154                     printed = true;
5155                     pw.println("user-stopped");
5156                 }
5157                 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
5158                     if (printed) {
5159                         pw.print(" ");
5160                     }
5161                     printed = true;
5162                     pw.println("source-user-stopped");
5163                 }
5164                 if (mBackingUpUids.get(js.getSourceUid())) {
5165                     if (printed) {
5166                         pw.print(" ");
5167                     }
5168                     printed = true;
5169                     pw.println("backing-up");
5170                 }
5171                 boolean componentPresent = false;
5172                 try {
5173                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
5174                             js.getServiceComponent(),
5175                             PackageManager.MATCH_DIRECT_BOOT_AUTO,
5176                             js.getUserId()) != null);
5177                 } catch (RemoteException e) {
5178                 }
5179                 if (!componentPresent) {
5180                     if (printed) {
5181                         pw.print(" ");
5182                     }
5183                     printed = true;
5184                     pw.println("no-component");
5185                 }
5186                 if (js.isReady()) {
5187                     if (printed) {
5188                         pw.print(" ");
5189                     }
5190                     printed = true;
5191                     pw.println("ready");
5192                 }
5193                 if (!printed) {
5194                     pw.print("waiting");
5195                 }
5196                 pw.println();
5197             }
5198         } catch (RemoteException e) {
5199             // can't happen
5200         }
5201         return 0;
5202     }
5203 
5204     void resetExecutionQuota(@NonNull String pkgName, int userId) {
5205         synchronized (mLock) {
5206             mQuotaController.clearAppStatsLocked(userId, pkgName);
5207         }
5208     }
5209 
5210     void resetScheduleQuota() {
5211         mQuotaTracker.clear();
5212     }
5213 
5214     void triggerDockState(boolean idleState) {
5215         final Intent dockIntent;
5216         if (idleState) {
5217             dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
5218         } else {
5219             dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
5220         }
5221         dockIntent.setPackage("android");
5222         dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
5223         getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
5224     }
5225 
5226     static void dumpHelp(PrintWriter pw) {
5227         pw.println("Job Scheduler (jobscheduler) dump options:");
5228         pw.println("  [-h] [package] ...");
5229         pw.println("    -h: print this help");
5230         pw.println("  [package] is an optional package name to limit the output to.");
5231     }
5232 
5233     /** Sort jobs by caller UID, then by Job ID. */
5234     private static void sortJobs(List<JobStatus> jobs) {
5235         Collections.sort(jobs, new Comparator<JobStatus>() {
5236             @Override
5237             public int compare(JobStatus o1, JobStatus o2) {
5238                 int uid1 = o1.getUid();
5239                 int uid2 = o2.getUid();
5240                 int id1 = o1.getJobId();
5241                 int id2 = o2.getJobId();
5242                 if (uid1 != uid2) {
5243                     return uid1 < uid2 ? -1 : 1;
5244                 }
5245                 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
5246             }
5247         });
5248     }
5249 
5250     @NeverCompile // Avoid size overhead of debugging code.
5251     void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
5252         final int filterAppId = UserHandle.getAppId(filterUid);
5253         final long now = sSystemClock.millis();
5254         final long nowElapsed = sElapsedRealtimeClock.millis();
5255         final long nowUptime = sUptimeMillisClock.millis();
5256 
5257         final Predicate<JobStatus> predicate = (js) -> {
5258             return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
5259                     || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
5260         };
5261         synchronized (mLock) {
5262             mConstants.dump(pw);
5263             for (StateController controller : mControllers) {
5264                 pw.increaseIndent();
5265                 controller.dumpConstants(pw);
5266                 pw.decreaseIndent();
5267             }
5268             pw.println();
5269 
5270             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
5271                 mJobRestrictions.get(i).dumpConstants(pw);
5272             }
5273             pw.println();
5274 
5275             mQuotaTracker.dump(pw);
5276             pw.println();
5277 
5278             pw.print("Battery charging: ");
5279             pw.println(mBatteryStateTracker.isCharging());
5280             pw.print("Battery not low: ");
5281             pw.println(mBatteryStateTracker.isBatteryNotLow());
5282             if (mBatteryStateTracker.isMonitoring()) {
5283                 pw.print("MONITORING: seq=");
5284                 pw.println(mBatteryStateTracker.getSeq());
5285             }
5286             pw.println();
5287 
5288             pw.println("Started users: " + Arrays.toString(mStartedUsers));
5289             pw.println();
5290 
5291             pw.print("Media Cloud Providers: ");
5292             pw.println(mCloudMediaProviderPackages);
5293             pw.println();
5294 
5295             pw.print("Registered ");
5296             pw.print(mJobs.size());
5297             pw.println(" jobs:");
5298             pw.increaseIndent();
5299             boolean jobPrinted = false;
5300             if (mJobs.size() > 0) {
5301                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
5302                 sortJobs(jobs);
5303                 for (JobStatus job : jobs) {
5304                     // Skip printing details if the caller requested a filter
5305                     if (!predicate.test(job)) {
5306                         continue;
5307                     }
5308                     jobPrinted = true;
5309 
5310                     pw.print("JOB ");
5311                     job.printUniqueId(pw);
5312                     pw.print(": ");
5313                     pw.println(job.toShortStringExceptUniqueId());
5314 
5315                     pw.increaseIndent();
5316                     job.dump(pw, true, nowElapsed);
5317 
5318                     pw.print("Restricted due to:");
5319                     final boolean isRestricted = checkIfRestricted(job) != null;
5320                     if (isRestricted) {
5321                         for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
5322                             final JobRestriction restriction = mJobRestrictions.get(i);
5323                             if (restriction.isJobRestricted(job)) {
5324                                 final int reason = restriction.getInternalReason();
5325                                 pw.print(" ");
5326                                 pw.print(JobParameters.getInternalReasonCodeDescription(reason));
5327                             }
5328                         }
5329                     } else {
5330                         pw.print(" none");
5331                     }
5332                     pw.println(".");
5333 
5334                     pw.print("Ready: ");
5335                     pw.print(isReadyToBeExecutedLocked(job));
5336                     pw.print(" (job=");
5337                     pw.print(job.isReady());
5338                     pw.print(" user=");
5339                     pw.print(areUsersStartedLocked(job));
5340                     pw.print(" !restricted=");
5341                     pw.print(!isRestricted);
5342                     pw.print(" !pending=");
5343                     pw.print(!mPendingJobQueue.contains(job));
5344                     pw.print(" !active=");
5345                     pw.print(!mConcurrencyManager.isJobRunningLocked(job));
5346                     pw.print(" !backingup=");
5347                     pw.print(!(mBackingUpUids.get(job.getSourceUid())));
5348                     pw.print(" comp=");
5349                     pw.print(isComponentUsable(job));
5350                     pw.println(")");
5351 
5352                     pw.decreaseIndent();
5353                 }
5354             }
5355             if (!jobPrinted) {
5356                 pw.println("None.");
5357             }
5358             pw.decreaseIndent();
5359 
5360             for (int i = 0; i < mControllers.size(); i++) {
5361                 pw.println();
5362                 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
5363                 pw.increaseIndent();
5364                 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
5365                 pw.decreaseIndent();
5366             }
5367 
5368             boolean procStatePrinted = false;
5369             for (int i = 0; i < mUidProcStates.size(); i++) {
5370                 int uid = mUidProcStates.keyAt(i);
5371                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5372                     if (!procStatePrinted) {
5373                         procStatePrinted = true;
5374                         pw.println();
5375                         pw.println("Uid proc states:");
5376                         pw.increaseIndent();
5377                     }
5378                     pw.print(UserHandle.formatUid(uid));
5379                     pw.print(": ");
5380                     pw.println(ActivityManager.procStateToString(mUidProcStates.valueAt(i)));
5381                 }
5382             }
5383             if (procStatePrinted) {
5384                 pw.decreaseIndent();
5385             }
5386 
5387             boolean overridePrinted = false;
5388             for (int i = 0; i < mUidBiasOverride.size(); i++) {
5389                 int uid = mUidBiasOverride.keyAt(i);
5390                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5391                     if (!overridePrinted) {
5392                         overridePrinted = true;
5393                         pw.println();
5394                         pw.println("Uid bias overrides:");
5395                         pw.increaseIndent();
5396                     }
5397                     pw.print(UserHandle.formatUid(uid));
5398                     pw.print(": "); pw.println(mUidBiasOverride.valueAt(i));
5399                 }
5400             }
5401             if (overridePrinted) {
5402                 pw.decreaseIndent();
5403             }
5404 
5405             boolean capabilitiesPrinted = false;
5406             for (int i = 0; i < mUidCapabilities.size(); i++) {
5407                 int uid = mUidCapabilities.keyAt(i);
5408                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5409                     if (!capabilitiesPrinted) {
5410                         capabilitiesPrinted = true;
5411                         pw.println();
5412                         pw.println("Uid capabilities:");
5413                         pw.increaseIndent();
5414                     }
5415                     pw.print(UserHandle.formatUid(uid));
5416                     pw.print(": ");
5417                     pw.println(ActivityManager.getCapabilitiesSummary(mUidCapabilities.valueAt(i)));
5418                 }
5419             }
5420             if (capabilitiesPrinted) {
5421                 pw.decreaseIndent();
5422             }
5423 
5424             boolean uidMapPrinted = false;
5425             for (int i = 0; i < mUidToPackageCache.size(); ++i) {
5426                 final int uid = mUidToPackageCache.keyAt(i);
5427                 if (filterUid != -1 && filterUid != uid) {
5428                     continue;
5429                 }
5430                 if (!uidMapPrinted) {
5431                     uidMapPrinted = true;
5432                     pw.println();
5433                     pw.println("Cached UID->package map:");
5434                     pw.increaseIndent();
5435                 }
5436                 pw.print(uid);
5437                 pw.print(": ");
5438                 pw.println(mUidToPackageCache.get(uid));
5439             }
5440             if (uidMapPrinted) {
5441                 pw.decreaseIndent();
5442             }
5443 
5444             boolean backingPrinted = false;
5445             for (int i = 0; i < mBackingUpUids.size(); i++) {
5446                 int uid = mBackingUpUids.keyAt(i);
5447                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5448                     if (!backingPrinted) {
5449                         pw.println();
5450                         pw.println("Backing up uids:");
5451                         pw.increaseIndent();
5452                         backingPrinted = true;
5453                     } else {
5454                         pw.print(", ");
5455                     }
5456                     pw.print(UserHandle.formatUid(uid));
5457                 }
5458             }
5459             if (backingPrinted) {
5460                 pw.decreaseIndent();
5461                 pw.println();
5462             }
5463 
5464             pw.println();
5465             mJobPackageTracker.dump(pw, filterAppId);
5466             pw.println();
5467             if (mJobPackageTracker.dumpHistory(pw, filterAppId)) {
5468                 pw.println();
5469             }
5470 
5471             boolean pendingPrinted = false;
5472             pw.println("Pending queue:");
5473             pw.increaseIndent();
5474             JobStatus job;
5475             int pendingIdx = 0;
5476             mPendingJobQueue.resetIterator();
5477             while ((job = mPendingJobQueue.next()) != null) {
5478                 pendingIdx++;
5479                 if (!predicate.test(job)) {
5480                     continue;
5481                 }
5482                 if (!pendingPrinted) {
5483                     pendingPrinted = true;
5484                 }
5485 
5486                 pw.print("Pending #"); pw.print(pendingIdx); pw.print(": ");
5487                 pw.println(job.toShortString());
5488 
5489                 pw.increaseIndent();
5490                 job.dump(pw, false, nowElapsed);
5491                 int bias = evaluateJobBiasLocked(job);
5492                 pw.print("Evaluated bias: ");
5493                 pw.println(JobInfo.getBiasString(bias));
5494 
5495                 pw.print("Tag: "); pw.println(job.getTag());
5496                 pw.print("Enq: ");
5497                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
5498                 pw.decreaseIndent();
5499                 pw.println();
5500             }
5501             if (!pendingPrinted) {
5502                 pw.println("None");
5503             }
5504             pw.decreaseIndent();
5505 
5506             pw.println();
5507             mConcurrencyManager.dumpContextInfoLocked(pw, predicate, nowElapsed, nowUptime);
5508 
5509             pw.println();
5510             boolean recentPrinted = false;
5511             pw.println("Recently completed jobs:");
5512             pw.increaseIndent();
5513             for (int r = 1; r <= NUM_COMPLETED_JOB_HISTORY; ++r) {
5514                 // Print most recent first
5515                 final int idx = (mLastCompletedJobIndex + NUM_COMPLETED_JOB_HISTORY - r)
5516                         % NUM_COMPLETED_JOB_HISTORY;
5517                 job = mLastCompletedJobs[idx];
5518                 if (job != null) {
5519                     if (!predicate.test(job)) {
5520                         continue;
5521                     }
5522                     recentPrinted = true;
5523                     TimeUtils.formatDuration(mLastCompletedJobTimeElapsed[idx], nowElapsed, pw);
5524                     pw.println();
5525                     // Double indent for readability
5526                     pw.increaseIndent();
5527                     pw.increaseIndent();
5528                     pw.println(job.toShortString());
5529                     job.dump(pw, true, nowElapsed);
5530                     pw.decreaseIndent();
5531                     pw.decreaseIndent();
5532                 }
5533             }
5534             if (!recentPrinted) {
5535                 pw.println("None");
5536             }
5537             pw.decreaseIndent();
5538             pw.println();
5539 
5540             boolean recentCancellationsPrinted = false;
5541             for (int r = 1; r <= mLastCancelledJobs.length; ++r) {
5542                 // Print most recent first
5543                 final int idx = (mLastCancelledJobIndex + mLastCancelledJobs.length - r)
5544                         % mLastCancelledJobs.length;
5545                 job = mLastCancelledJobs[idx];
5546                 if (job != null) {
5547                     if (!predicate.test(job)) {
5548                         continue;
5549                     }
5550                     if (!recentCancellationsPrinted) {
5551                         pw.println();
5552                         pw.println("Recently cancelled jobs:");
5553                         pw.increaseIndent();
5554                         recentCancellationsPrinted = true;
5555                     }
5556                     TimeUtils.formatDuration(mLastCancelledJobTimeElapsed[idx], nowElapsed, pw);
5557                     pw.println();
5558                     // Double indent for readability
5559                     pw.increaseIndent();
5560                     pw.increaseIndent();
5561                     pw.println(job.toShortString());
5562                     job.dump(pw, true, nowElapsed);
5563                     pw.decreaseIndent();
5564                     pw.decreaseIndent();
5565                 }
5566             }
5567             if (!recentCancellationsPrinted) {
5568                 pw.decreaseIndent();
5569                 pw.println();
5570             }
5571 
5572             if (filterUid == -1) {
5573                 pw.println();
5574                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
5575                 pw.print("mReportedActive="); pw.println(mReportedActive);
5576             }
5577             pw.println();
5578 
5579             mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
5580 
5581             pw.println();
5582             pw.print("PersistStats: ");
5583             pw.println(mJobs.getPersistStats());
5584         }
5585         pw.println();
5586     }
5587 
5588     void dumpInternalProto(final FileDescriptor fd, int filterUid) {
5589         ProtoOutputStream proto = new ProtoOutputStream(fd);
5590         final int filterAppId = UserHandle.getAppId(filterUid);
5591         final long now = sSystemClock.millis();
5592         final long nowElapsed = sElapsedRealtimeClock.millis();
5593         final long nowUptime = sUptimeMillisClock.millis();
5594         final Predicate<JobStatus> predicate = (js) -> {
5595             return filterAppId == -1 || UserHandle.getAppId(js.getUid()) == filterAppId
5596                     || UserHandle.getAppId(js.getSourceUid()) == filterAppId;
5597         };
5598 
5599         synchronized (mLock) {
5600             final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS);
5601             mConstants.dump(proto);
5602             for (StateController controller : mControllers) {
5603                 controller.dumpConstants(proto);
5604             }
5605             proto.end(settingsToken);
5606 
5607             for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
5608                 mJobRestrictions.get(i).dumpConstants(proto);
5609             }
5610 
5611             for (int u : mStartedUsers) {
5612                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
5613             }
5614 
5615             mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER);
5616 
5617             if (mJobs.size() > 0) {
5618                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
5619                 sortJobs(jobs);
5620                 for (JobStatus job : jobs) {
5621                     final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
5622                     job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
5623 
5624                     // Skip printing details if the caller requested a filter
5625                     if (!predicate.test(job)) {
5626                         continue;
5627                     }
5628 
5629                     job.dump(proto,
5630                             JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
5631 
5632                     proto.write(
5633                             JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
5634                             isReadyToBeExecutedLocked(job));
5635                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
5636                             job.isReady());
5637                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
5638                             areUsersStartedLocked(job));
5639                     proto.write(
5640                             JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
5641                             checkIfRestricted(job) != null);
5642                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
5643                             mPendingJobQueue.contains(job));
5644                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
5645                             mConcurrencyManager.isJobRunningLocked(job));
5646                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
5647                             mBackingUpUids.get(job.getSourceUid()));
5648                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
5649                             isComponentUsable(job));
5650 
5651                     for (JobRestriction restriction : mJobRestrictions) {
5652                         final long restrictionsToken = proto.start(
5653                                 JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
5654                         proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
5655                                 restriction.getInternalReason());
5656                         proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
5657                                 restriction.isJobRestricted(job));
5658                         proto.end(restrictionsToken);
5659                     }
5660 
5661                     proto.end(rjToken);
5662                 }
5663             }
5664             for (StateController controller : mControllers) {
5665                 controller.dumpControllerStateLocked(
5666                         proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
5667             }
5668             for (int i = 0; i < mUidBiasOverride.size(); i++) {
5669                 int uid = mUidBiasOverride.keyAt(i);
5670                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5671                     long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
5672                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
5673                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
5674                             mUidBiasOverride.valueAt(i));
5675                     proto.end(pToken);
5676                 }
5677             }
5678             for (int i = 0; i < mBackingUpUids.size(); i++) {
5679                 int uid = mBackingUpUids.keyAt(i);
5680                 if (filterAppId == -1 || filterAppId == UserHandle.getAppId(uid)) {
5681                     proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
5682                 }
5683             }
5684 
5685             mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
5686                     filterAppId);
5687             mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
5688                     filterAppId);
5689 
5690             JobStatus job;
5691             mPendingJobQueue.resetIterator();
5692             while ((job = mPendingJobQueue.next()) != null) {
5693                 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
5694 
5695                 job.writeToShortProto(proto, PendingJob.INFO);
5696                 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
5697                 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobBiasLocked(job));
5698                 proto.write(PendingJob.PENDING_DURATION_MS, nowUptime - job.madePending);
5699 
5700                 proto.end(pjToken);
5701             }
5702             if (filterUid == -1) {
5703                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
5704                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
5705             }
5706             mConcurrencyManager.dumpProtoLocked(proto,
5707                     JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
5708 
5709             mJobs.getPersistStats().dumpDebug(proto, JobSchedulerServiceDumpProto.PERSIST_STATS);
5710         }
5711 
5712         proto.flush();
5713     }
5714 }
5715