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