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