• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.job;
18 
19 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20 import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21 
22 import android.annotation.UserIdInt;
23 import android.app.Activity;
24 import android.app.ActivityManager;
25 import android.app.ActivityManagerInternal;
26 import android.app.AlarmManager;
27 import android.app.AppGlobals;
28 import android.app.IUidObserver;
29 import android.app.job.IJobScheduler;
30 import android.app.job.JobInfo;
31 import android.app.job.JobParameters;
32 import android.app.job.JobProtoEnums;
33 import android.app.job.JobScheduler;
34 import android.app.job.JobService;
35 import android.app.job.JobWorkItem;
36 import android.app.usage.UsageStatsManager;
37 import android.app.usage.UsageStatsManagerInternal;
38 import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
39 import android.content.BroadcastReceiver;
40 import android.content.ComponentName;
41 import android.content.ContentResolver;
42 import android.content.Context;
43 import android.content.Intent;
44 import android.content.IntentFilter;
45 import android.content.pm.IPackageManager;
46 import android.content.pm.PackageManager;
47 import android.content.pm.PackageManager.NameNotFoundException;
48 import android.content.pm.PackageManagerInternal;
49 import android.content.pm.ServiceInfo;
50 import android.database.ContentObserver;
51 import android.net.Uri;
52 import android.os.BatteryStats;
53 import android.os.BatteryStatsInternal;
54 import android.os.Binder;
55 import android.os.Handler;
56 import android.os.Looper;
57 import android.os.Message;
58 import android.os.Process;
59 import android.os.RemoteException;
60 import android.os.ResultReceiver;
61 import android.os.ServiceManager;
62 import android.os.ShellCallback;
63 import android.os.SystemClock;
64 import android.os.UserHandle;
65 import android.os.UserManagerInternal;
66 import android.provider.Settings;
67 import android.text.format.DateUtils;
68 import android.util.KeyValueListParser;
69 import android.util.Log;
70 import android.util.Slog;
71 import android.util.SparseArray;
72 import android.util.SparseIntArray;
73 import android.util.StatsLog;
74 import android.util.TimeUtils;
75 import android.util.proto.ProtoOutputStream;
76 
77 import com.android.internal.annotations.VisibleForTesting;
78 import com.android.internal.app.IBatteryStats;
79 import com.android.internal.app.procstats.ProcessStats;
80 import com.android.internal.os.BackgroundThread;
81 import com.android.internal.util.ArrayUtils;
82 import com.android.internal.util.DumpUtils;
83 import com.android.internal.util.IndentingPrintWriter;
84 import com.android.internal.util.Preconditions;
85 import com.android.server.AppStateTracker;
86 import com.android.server.DeviceIdleController;
87 import com.android.server.FgThread;
88 import com.android.server.LocalServices;
89 import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
90 import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
91 import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
92 import com.android.server.job.controllers.BackgroundJobsController;
93 import com.android.server.job.controllers.BatteryController;
94 import com.android.server.job.controllers.ConnectivityController;
95 import com.android.server.job.controllers.ContentObserverController;
96 import com.android.server.job.controllers.DeviceIdleJobsController;
97 import com.android.server.job.controllers.IdleController;
98 import com.android.server.job.controllers.JobStatus;
99 import com.android.server.job.controllers.StateController;
100 import com.android.server.job.controllers.StorageController;
101 import com.android.server.job.controllers.TimeController;
102 
103 import libcore.util.EmptyArray;
104 
105 import java.io.FileDescriptor;
106 import java.io.PrintWriter;
107 import java.time.Clock;
108 import java.util.ArrayList;
109 import java.util.Arrays;
110 import java.util.Collections;
111 import java.util.Comparator;
112 import java.util.HashMap;
113 import java.util.Iterator;
114 import java.util.List;
115 import java.util.function.Consumer;
116 import java.util.function.Predicate;
117 
118 /**
119  * Responsible for taking jobs representing work to be performed by a client app, and determining
120  * based on the criteria specified when that job should be run against the client application's
121  * endpoint.
122  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
123  * about constraints, or the state of active jobs. It receives callbacks from the various
124  * controllers and completed jobs and operates accordingly.
125  *
126  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
127  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
128  * @hide
129  */
130 public class JobSchedulerService extends com.android.server.SystemService
131         implements StateChangedListener, JobCompletedListener {
132     public static final String TAG = "JobScheduler";
133     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
134     public static final boolean DEBUG_STANDBY = DEBUG || false;
135 
136     /** The maximum number of concurrent jobs we run at one time. */
137     private static final int MAX_JOB_CONTEXTS_COUNT = 16;
138     /** Enforce a per-app limit on scheduled jobs? */
139     private static final boolean ENFORCE_MAX_JOBS = true;
140     /** The maximum number of jobs that we allow an unprivileged app to schedule */
141     private static final int MAX_JOBS_PER_APP = 100;
142 
143     @VisibleForTesting
144     public static Clock sSystemClock = Clock.systemUTC();
145     @VisibleForTesting
146     public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
147     @VisibleForTesting
148     public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
149 
150     /** Global local for all job scheduler state. */
151     final Object mLock = new Object();
152     /** Master list of jobs. */
153     final JobStore mJobs;
154     /** Tracking the standby bucket state of each app */
155     final StandbyTracker mStandbyTracker;
156     /** Tracking amount of time each package runs for. */
157     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
158 
159     static final int MSG_JOB_EXPIRED = 0;
160     static final int MSG_CHECK_JOB = 1;
161     static final int MSG_STOP_JOB = 2;
162     static final int MSG_CHECK_JOB_GREEDY = 3;
163     static final int MSG_UID_STATE_CHANGED = 4;
164     static final int MSG_UID_GONE = 5;
165     static final int MSG_UID_ACTIVE = 6;
166     static final int MSG_UID_IDLE = 7;
167 
168     /**
169      * Track Services that have currently active or pending jobs. The index is provided by
170      * {@link JobStatus#getServiceToken()}
171      */
172     final List<JobServiceContext> mActiveServices = new ArrayList<>();
173 
174     /** List of controllers that will notify this service of updates to jobs. */
175     private final List<StateController> mControllers;
176     /** Need direct access to this for testing. */
177     private final BatteryController mBatteryController;
178     /** Need direct access to this for testing. */
179     private final StorageController mStorageController;
180     /** Need directly for sending uid state changes */
181     private final DeviceIdleJobsController mDeviceIdleJobsController;
182 
183     /**
184      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
185      * when ready to execute them.
186      */
187     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
188 
189     int[] mStartedUsers = EmptyArray.INT;
190 
191     final JobHandler mHandler;
192     final JobSchedulerStub mJobSchedulerStub;
193 
194     PackageManagerInternal mLocalPM;
195     ActivityManagerInternal mActivityManagerInternal;
196     IBatteryStats mBatteryStats;
197     DeviceIdleController.LocalService mLocalDeviceIdleController;
198     AppStateTracker mAppStateTracker;
199     final UsageStatsManagerInternal mUsageStats;
200 
201     /**
202      * Set to true once we are allowed to run third party apps.
203      */
204     boolean mReadyToRock;
205 
206     /**
207      * What we last reported to DeviceIdleController about whether we are active.
208      */
209     boolean mReportedActive;
210 
211     /**
212      * Are we currently in device-wide standby parole?
213      */
214     volatile boolean mInParole;
215 
216     /**
217      * Current limit on the number of concurrent JobServiceContext entries we want to
218      * keep actively running a job.
219      */
220     int mMaxActiveJobs = 1;
221 
222     /**
223      * A mapping of which uids are currently in the foreground to their effective priority.
224      */
225     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
226 
227     /**
228      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
229      */
230     final SparseIntArray mBackingUpUids = new SparseIntArray();
231 
232     /**
233      * Count standby heartbeats, and keep track of which beat each bucket's jobs will
234      * next become runnable.  Index into this array is by normalized bucket:
235      * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }.  The ACTIVE and NEVER bucket
236      * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
237      * and NEVER apps don't get them at all.
238      */
239     final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
240     long mHeartbeat = 0;
241     long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
242 
243     /**
244      * Named indices into the STANDBY_BEATS array, for clarity in referring to
245      * specific buckets' bookkeeping.
246      */
247     static final int ACTIVE_INDEX = 0;
248     static final int WORKING_INDEX = 1;
249     static final int FREQUENT_INDEX = 2;
250     static final int RARE_INDEX = 3;
251     static final int NEVER_INDEX = 4;
252 
253     /**
254      * Bookkeeping about when jobs last run.  We keep our own record in heartbeat time,
255      * rather than rely on Usage Stats' timestamps, because heartbeat time can be
256      * manipulated for testing purposes and we need job runnability to track that rather
257      * than real time.
258      *
259      * Outer SparseArray slices by user handle; inner map of package name to heartbeat
260      * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
261      * and it will be accessed in a known-hot code path.
262      */
263     final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
264 
265     static final String HEARTBEAT_TAG = "*job.heartbeat*";
266     final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
267 
268     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
269 
270     /**
271      * This array essentially stores the state of mActiveServices array.
272      * The ith index stores the job present on the ith JobServiceContext.
273      * We manipulate this array until we arrive at what jobs should be running on
274      * what JobServiceContext.
275      */
276     JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
277     /**
278      * Indicates whether we need to act on this jobContext id
279      */
280     boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
281     /**
282      * The uid whose jobs we would like to assign to a context.
283      */
284     int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
285 
286     private class ConstantsObserver extends ContentObserver {
287         private ContentResolver mResolver;
288 
ConstantsObserver(Handler handler)289         public ConstantsObserver(Handler handler) {
290             super(handler);
291         }
292 
start(ContentResolver resolver)293         public void start(ContentResolver resolver) {
294             mResolver = resolver;
295             mResolver.registerContentObserver(Settings.Global.getUriFor(
296                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
297             updateConstants();
298         }
299 
300         @Override
onChange(boolean selfChange, Uri uri)301         public void onChange(boolean selfChange, Uri uri) {
302             updateConstants();
303         }
304 
updateConstants()305         private void updateConstants() {
306             synchronized (mLock) {
307                 try {
308                     mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
309                             Settings.Global.JOB_SCHEDULER_CONSTANTS));
310                 } catch (IllegalArgumentException e) {
311                     // Failed to parse the settings string, log this and move on
312                     // with defaults.
313                     Slog.e(TAG, "Bad jobscheduler settings", e);
314                 }
315             }
316 
317             // Reset the heartbeat alarm based on the new heartbeat duration
318             setNextHeartbeatAlarm();
319         }
320     }
321 
322     /**
323      * All times are in milliseconds. These constants are kept synchronized with the system
324      * global Settings. Any access to this class or its fields should be done while
325      * holding the JobSchedulerService.mLock lock.
326      */
327     public static class Constants {
328         // Key names stored in the settings value.
329         private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
330         private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
331         private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
332         private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
333         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
334         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
335         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
336         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
337         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
338         private static final String KEY_FG_JOB_COUNT = "fg_job_count";
339         private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
340         private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
341         private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
342         private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
343         private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
344                 = "max_standard_reschedule_count";
345         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
346         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
347         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
348         private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
349         private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
350         private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
351         private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
352         private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
353         private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
354 
355         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
356         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
357         private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
358         private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
359         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
360         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
361         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
362         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
363         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
364         private static final int DEFAULT_FG_JOB_COUNT = 4;
365         private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
366         private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
367         private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
368         private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
369         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
370         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
371         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
372         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
373         private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
374         private static final int DEFAULT_STANDBY_WORKING_BEATS = 11;  // ~ 2 hours, with 11min beats
375         private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
376         private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
377         private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
378         private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
379 
380         /**
381          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
382          * early.
383          */
384         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
385         /**
386          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
387          * things early.
388          */
389         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
390         /**
391          * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
392          * schedule things early.
393          */
394         int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
395         /**
396          * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
397          * schedule things early.
398          */
399         int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
400         /**
401          * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
402          * things early.  1 == Run connectivity jobs as soon as ready.
403          */
404         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
405         /**
406          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
407          * schedule things early.
408          */
409         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
410         /**
411          * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
412          * running some work early.  This (and thus the other min counts) is now set to 1, to
413          * prevent any batching at this level.  Since we now do batching through doze, that is
414          * a much better mechanism.
415          */
416         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
417         /**
418          * This is the job execution factor that is considered to be heavy use of the system.
419          */
420         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
421         /**
422          * This is the job execution factor that is considered to be moderate use of the system.
423          */
424         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
425         /**
426          * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
427          */
428         int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
429         /**
430          * The maximum number of background jobs we allow when the system is in a normal
431          * memory state.
432          */
433         int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
434         /**
435          * The maximum number of background jobs we allow when the system is in a moderate
436          * memory state.
437          */
438         int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
439         /**
440          * The maximum number of background jobs we allow when the system is in a low
441          * memory state.
442          */
443         int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
444         /**
445          * The maximum number of background jobs we allow when the system is in a critical
446          * memory state.
447          */
448         int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
449         /**
450          * The maximum number of times we allow a job to have itself rescheduled before
451          * giving up on it, for standard jobs.
452          */
453         int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
454         /**
455          * The maximum number of times we allow a job to have itself rescheduled before
456          * giving up on it, for jobs that are executing work.
457          */
458         int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
459         /**
460          * The minimum backoff time to allow for linear backoff.
461          */
462         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
463         /**
464          * The minimum backoff time to allow for exponential backoff.
465          */
466         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
467         /**
468          * How often we recalculate runnability based on apps' standby bucket assignment.
469          * This should be prime relative to common time interval lengths such as a quarter-
470          * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
471          */
472         long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
473         /**
474          * Mapping: standby bucket -> number of heartbeats between each sweep of that
475          * bucket's jobs.
476          *
477          * Bucket assignments as recorded in the JobStatus objects are normalized to be
478          * indices into this array, rather than the raw constants used
479          * by AppIdleHistory.
480          */
481         final int[] STANDBY_BEATS = {
482                 0,
483                 DEFAULT_STANDBY_WORKING_BEATS,
484                 DEFAULT_STANDBY_FREQUENT_BEATS,
485                 DEFAULT_STANDBY_RARE_BEATS
486         };
487         /**
488          * The fraction of a job's running window that must pass before we
489          * consider running it when the network is congested.
490          */
491         public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
492         /**
493          * The fraction of a prefetch job's running window that must pass before
494          * we consider matching it against a metered network.
495          */
496         public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
497 
498         private final KeyValueListParser mParser = new KeyValueListParser(',');
499 
updateConstantsLocked(String value)500         void updateConstantsLocked(String value) {
501             try {
502                 mParser.setString(value);
503             } catch (Exception e) {
504                 // Failed to parse the settings string, log this and move on
505                 // with defaults.
506                 Slog.e(TAG, "Bad jobscheduler settings", e);
507             }
508 
509             MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
510                     DEFAULT_MIN_IDLE_COUNT);
511             MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
512                     DEFAULT_MIN_CHARGING_COUNT);
513             MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
514                     DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
515             MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
516                     DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
517             MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
518                     DEFAULT_MIN_CONNECTIVITY_COUNT);
519             MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
520                     DEFAULT_MIN_CONTENT_COUNT);
521             MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
522                     DEFAULT_MIN_READY_JOBS_COUNT);
523             HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
524                     DEFAULT_HEAVY_USE_FACTOR);
525             MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
526                     DEFAULT_MODERATE_USE_FACTOR);
527             FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
528                     DEFAULT_FG_JOB_COUNT);
529             BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
530                     DEFAULT_BG_NORMAL_JOB_COUNT);
531             if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
532                 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
533             }
534             BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
535                     DEFAULT_BG_MODERATE_JOB_COUNT);
536             if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
537                 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
538             }
539             BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
540                     DEFAULT_BG_LOW_JOB_COUNT);
541             if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
542                 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
543             }
544             BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
545                     DEFAULT_BG_CRITICAL_JOB_COUNT);
546             if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
547                 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
548             }
549             MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
550                     DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
551             MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
552                     DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
553             MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
554                     DEFAULT_MIN_LINEAR_BACKOFF_TIME);
555             MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
556                     DEFAULT_MIN_EXP_BACKOFF_TIME);
557             STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
558                     DEFAULT_STANDBY_HEARTBEAT_TIME);
559             STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
560                     DEFAULT_STANDBY_WORKING_BEATS);
561             STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
562                     DEFAULT_STANDBY_FREQUENT_BEATS);
563             STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
564                     DEFAULT_STANDBY_RARE_BEATS);
565             CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
566                     DEFAULT_CONN_CONGESTION_DELAY_FRAC);
567             CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
568                     DEFAULT_CONN_PREFETCH_RELAX_FRAC);
569         }
570 
dump(IndentingPrintWriter pw)571         void dump(IndentingPrintWriter pw) {
572             pw.println("Settings:");
573             pw.increaseIndent();
574             pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
575             pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
576             pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
577             pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
578             pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
579             pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
580             pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
581             pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
582             pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
583             pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
584             pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
585             pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
586             pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
587             pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
588             pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
589             pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
590             pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
591             pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
592             pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
593             pw.print("standby_beats={");
594             pw.print(STANDBY_BEATS[0]);
595             for (int i = 1; i < STANDBY_BEATS.length; i++) {
596                 pw.print(", ");
597                 pw.print(STANDBY_BEATS[i]);
598             }
599             pw.println('}');
600             pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
601             pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
602             pw.decreaseIndent();
603         }
604 
dump(ProtoOutputStream proto, long fieldId)605         void dump(ProtoOutputStream proto, long fieldId) {
606             final long token = proto.start(fieldId);
607             proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
608             proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
609             proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
610             proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
611             proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
612             proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
613             proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
614             proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
615             proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
616             proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
617             proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
618             proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
619             proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
620             proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
621             proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
622             proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
623             proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
624             proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
625             proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
626             for (int period : STANDBY_BEATS) {
627                 proto.write(ConstantsProto.STANDBY_BEATS, period);
628             }
629             proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
630             proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
631             proto.end(token);
632         }
633     }
634 
635     final Constants mConstants;
636     final ConstantsObserver mConstantsObserver;
637 
638     static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
639         if (o1.enqueueTime < o2.enqueueTime) {
640             return -1;
641         }
642         return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
643     };
644 
addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator)645     static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
646         int where = Collections.binarySearch(array, newItem, comparator);
647         if (where < 0) {
648             where = ~where;
649         }
650         array.add(where, newItem);
651     }
652 
653     /**
654      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
655      * still clean up. On reinstall the package will have a new uid.
656      */
657     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
658         @Override
659         public void onReceive(Context context, Intent intent) {
660             final String action = intent.getAction();
661             if (DEBUG) {
662                 Slog.d(TAG, "Receieved: " + action);
663             }
664             final String pkgName = getPackageName(intent);
665             final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
666 
667             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
668                 // Purge the app's jobs if the whole package was just disabled.  When this is
669                 // the case the component name will be a bare package name.
670                 if (pkgName != null && pkgUid != -1) {
671                     final String[] changedComponents = intent.getStringArrayExtra(
672                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
673                     if (changedComponents != null) {
674                         for (String component : changedComponents) {
675                             if (component.equals(pkgName)) {
676                                 if (DEBUG) {
677                                     Slog.d(TAG, "Package state change: " + pkgName);
678                                 }
679                                 try {
680                                     final int userId = UserHandle.getUserId(pkgUid);
681                                     IPackageManager pm = AppGlobals.getPackageManager();
682                                     final int state = pm.getApplicationEnabledSetting(pkgName, userId);
683                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
684                                             || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
685                                         if (DEBUG) {
686                                             Slog.d(TAG, "Removing jobs for package " + pkgName
687                                                     + " in user " + userId);
688                                         }
689                                         cancelJobsForPackageAndUid(pkgName, pkgUid,
690                                                 "app disabled");
691                                     }
692                                 } catch (RemoteException|IllegalArgumentException e) {
693                                     /*
694                                      * IllegalArgumentException means that the package doesn't exist.
695                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
696                                      * behind outright uninstall, so by the time we try to act it's gone.
697                                      * We don't need to act on this PACKAGE_CHANGED when this happens;
698                                      * we'll get a PACKAGE_REMOVED later and clean up then.
699                                      *
700                                      * RemoteException can't actually happen; the package manager is
701                                      * running in this same process.
702                                      */
703                                 }
704                                 break;
705                             }
706                         }
707                     }
708                 } else {
709                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
710                 }
711             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
712                 // If this is an outright uninstall rather than the first half of an
713                 // app update sequence, cancel the jobs associated with the app.
714                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
715                     int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
716                     if (DEBUG) {
717                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
718                     }
719                     cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
720                 }
721             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
722                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
723                 if (DEBUG) {
724                     Slog.d(TAG, "Removing jobs for user: " + userId);
725                 }
726                 cancelJobsForUser(userId);
727             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
728                 // Has this package scheduled any jobs, such that we will take action
729                 // if it were to be force-stopped?
730                 if (pkgUid != -1) {
731                     List<JobStatus> jobsForUid;
732                     synchronized (mLock) {
733                         jobsForUid = mJobs.getJobsByUid(pkgUid);
734                     }
735                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
736                         if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
737                             if (DEBUG) {
738                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
739                                         + pkgUid + " has jobs");
740                             }
741                             setResultCode(Activity.RESULT_OK);
742                             break;
743                         }
744                     }
745                 }
746             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
747                 // possible force-stop
748                 if (pkgUid != -1) {
749                     if (DEBUG) {
750                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
751                     }
752                     cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
753                 }
754             }
755         }
756     };
757 
getPackageName(Intent intent)758     private String getPackageName(Intent intent) {
759         Uri uri = intent.getData();
760         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
761         return pkg;
762     }
763 
764     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
765         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
766             mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
767         }
768 
769         @Override public void onUidGone(int uid, boolean disabled) {
770             mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
771         }
772 
773         @Override public void onUidActive(int uid) throws RemoteException {
774             mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
775         }
776 
777         @Override public void onUidIdle(int uid, boolean disabled) {
778             mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
779         }
780 
781         @Override public void onUidCachedChanged(int uid, boolean cached) {
782         }
783     };
784 
getTestableContext()785     public Context getTestableContext() {
786         return getContext();
787     }
788 
getLock()789     public Object getLock() {
790         return mLock;
791     }
792 
getJobStore()793     public JobStore getJobStore() {
794         return mJobs;
795     }
796 
getConstants()797     public Constants getConstants() {
798         return mConstants;
799     }
800 
801     @Override
onStartUser(int userHandle)802     public void onStartUser(int userHandle) {
803         mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
804         // Let's kick any outstanding jobs for this user.
805         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
806     }
807 
808     @Override
onUnlockUser(int userHandle)809     public void onUnlockUser(int userHandle) {
810         // Let's kick any outstanding jobs for this user.
811         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
812     }
813 
814     @Override
onStopUser(int userHandle)815     public void onStopUser(int userHandle) {
816         mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
817     }
818 
819     /**
820      * Return whether an UID is active or idle.
821      */
isUidActive(int uid)822     private boolean isUidActive(int uid) {
823         return mAppStateTracker.isUidActiveSynced(uid);
824     }
825 
826     private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
827 
scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, int userId, String tag)828     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
829             int userId, String tag) {
830         try {
831             if (ActivityManager.getService().isAppStartModeDisabled(uId,
832                     job.getService().getPackageName())) {
833                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
834                         + " -- package not allowed to start");
835                 return JobScheduler.RESULT_FAILURE;
836             }
837         } catch (RemoteException e) {
838         }
839 
840         synchronized (mLock) {
841             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
842 
843             if (work != null && toCancel != null) {
844                 // Fast path: we are adding work to an existing job, and the JobInfo is not
845                 // changing.  We can just directly enqueue this work in to the job.
846                 if (toCancel.getJob().equals(job)) {
847 
848                     toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
849 
850                     // If any of work item is enqueued when the source is in the foreground,
851                     // exempt the entire job.
852                     toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
853 
854                     return JobScheduler.RESULT_SUCCESS;
855                 }
856             }
857 
858             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
859 
860             // Give exemption if the source is in the foreground just now.
861             // Note if it's a sync job, this method is called on the handler so it's not exactly
862             // the state when requestSync() was called, but that should be fine because of the
863             // 1 minute foreground grace period.
864             jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
865 
866             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
867             // Jobs on behalf of others don't apply to the per-app job cap
868             if (ENFORCE_MAX_JOBS && packageName == null) {
869                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
870                     Slog.w(TAG, "Too many jobs for uid " + uId);
871                     throw new IllegalStateException("Apps may not schedule more than "
872                                 + MAX_JOBS_PER_APP + " distinct jobs");
873                 }
874             }
875 
876             // This may throw a SecurityException.
877             jobStatus.prepareLocked(ActivityManager.getService());
878 
879             if (toCancel != null) {
880                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
881             }
882             if (work != null) {
883                 // If work has been supplied, enqueue it into the new job.
884                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
885             }
886             startTrackingJobLocked(jobStatus, toCancel);
887             StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
888                     uId, null, jobStatus.getBatteryName(),
889                     StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
890                     JobProtoEnums.STOP_REASON_CANCELLED);
891 
892             // If the job is immediately ready to run, then we can just immediately
893             // put it in the pending list and try to schedule it.  This is especially
894             // important for jobs with a 0 deadline constraint, since they will happen a fair
895             // amount, we want to handle them as quickly as possible, and semantically we want to
896             // make sure we have started holding the wake lock for the job before returning to
897             // the caller.
898             // If the job is not yet ready to run, there is nothing more to do -- we are
899             // now just waiting for one of its controllers to change state and schedule
900             // the job appropriately.
901             if (isReadyToBeExecutedLocked(jobStatus)) {
902                 // This is a new job, we can just immediately put it on the pending
903                 // list and try to run it.
904                 mJobPackageTracker.notePending(jobStatus);
905                 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
906                 maybeRunPendingJobsLocked();
907             }
908         }
909         return JobScheduler.RESULT_SUCCESS;
910     }
911 
getPendingJobs(int uid)912     public List<JobInfo> getPendingJobs(int uid) {
913         synchronized (mLock) {
914             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
915             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
916             for (int i = jobs.size() - 1; i >= 0; i--) {
917                 JobStatus job = jobs.get(i);
918                 outList.add(job.getJob());
919             }
920             return outList;
921         }
922     }
923 
getPendingJob(int uid, int jobId)924     public JobInfo getPendingJob(int uid, int jobId) {
925         synchronized (mLock) {
926             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
927             for (int i = jobs.size() - 1; i >= 0; i--) {
928                 JobStatus job = jobs.get(i);
929                 if (job.getJobId() == jobId) {
930                     return job.getJob();
931                 }
932             }
933             return null;
934         }
935     }
936 
cancelJobsForUser(int userHandle)937     void cancelJobsForUser(int userHandle) {
938         synchronized (mLock) {
939             final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
940             for (int i=0; i<jobsForUser.size(); i++) {
941                 JobStatus toRemove = jobsForUser.get(i);
942                 cancelJobImplLocked(toRemove, null, "user removed");
943             }
944         }
945     }
946 
cancelJobsForNonExistentUsers()947     private void cancelJobsForNonExistentUsers() {
948         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
949         synchronized (mLock) {
950             mJobs.removeJobsOfNonUsers(umi.getUserIds());
951         }
952     }
953 
cancelJobsForPackageAndUid(String pkgName, int uid, String reason)954     void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
955         if ("android".equals(pkgName)) {
956             Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
957             return;
958         }
959         synchronized (mLock) {
960             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
961             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
962                 final JobStatus job = jobsForUid.get(i);
963                 if (job.getSourcePackageName().equals(pkgName)) {
964                     cancelJobImplLocked(job, null, reason);
965                 }
966             }
967         }
968     }
969 
970     /**
971      * Entry point from client to cancel all jobs originating from their uid.
972      * This will remove the job from the master list, and cancel the job if it was staged for
973      * execution or being executed.
974      * @param uid Uid to check against for removal of a job.
975      *
976      */
cancelJobsForUid(int uid, String reason)977     public boolean cancelJobsForUid(int uid, String reason) {
978         if (uid == Process.SYSTEM_UID) {
979             Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
980             return false;
981         }
982 
983         boolean jobsCanceled = false;
984         synchronized (mLock) {
985             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
986             for (int i=0; i<jobsForUid.size(); i++) {
987                 JobStatus toRemove = jobsForUid.get(i);
988                 cancelJobImplLocked(toRemove, null, reason);
989                 jobsCanceled = true;
990             }
991         }
992         return jobsCanceled;
993     }
994 
995     /**
996      * Entry point from client to cancel the job corresponding to the jobId provided.
997      * This will remove the job from the master list, and cancel the job if it was staged for
998      * execution or being executed.
999      * @param uid Uid of the calling client.
1000      * @param jobId Id of the job, provided at schedule-time.
1001      */
cancelJob(int uid, int jobId, int callingUid)1002     public boolean cancelJob(int uid, int jobId, int callingUid) {
1003         JobStatus toCancel;
1004         synchronized (mLock) {
1005             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
1006             if (toCancel != null) {
1007                 cancelJobImplLocked(toCancel, null,
1008                         "cancel() called by app, callingUid=" + callingUid
1009                         + " uid=" + uid + " jobId=" + jobId);
1010             }
1011             return (toCancel != null);
1012         }
1013     }
1014 
cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason)1015     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
1016         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1017         cancelled.unprepareLocked(ActivityManager.getService());
1018         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1019         // Remove from pending queue.
1020         if (mPendingJobs.remove(cancelled)) {
1021             mJobPackageTracker.noteNonpending(cancelled);
1022         }
1023         // Cancel if running.
1024         stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
1025         reportActiveLocked();
1026     }
1027 
updateUidState(int uid, int procState)1028     void updateUidState(int uid, int procState) {
1029         synchronized (mLock) {
1030             if (procState == ActivityManager.PROCESS_STATE_TOP) {
1031                 // Only use this if we are exactly the top app.  All others can live
1032                 // with just the foreground priority.  This means that persistent processes
1033                 // can never be the top app priority...  that is fine.
1034                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
1035             } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
1036                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
1037             } else {
1038                 mUidPriorityOverride.delete(uid);
1039             }
1040         }
1041     }
1042 
1043     @Override
onDeviceIdleStateChanged(boolean deviceIdle)1044     public void onDeviceIdleStateChanged(boolean deviceIdle) {
1045         synchronized (mLock) {
1046             if (deviceIdle) {
1047                 // When becoming idle, make sure no jobs are actively running,
1048                 // except those using the idle exemption flag.
1049                 for (int i=0; i<mActiveServices.size(); i++) {
1050                     JobServiceContext jsc = mActiveServices.get(i);
1051                     final JobStatus executing = jsc.getRunningJobLocked();
1052                     if (executing != null
1053                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
1054                         jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1055                                 "cancelled due to doze");
1056                     }
1057                 }
1058             } else {
1059                 // When coming out of idle, allow thing to start back up.
1060                 if (mReadyToRock) {
1061                     if (mLocalDeviceIdleController != null) {
1062                         if (!mReportedActive) {
1063                             mReportedActive = true;
1064                             mLocalDeviceIdleController.setJobsActive(true);
1065                         }
1066                     }
1067                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1068                 }
1069             }
1070         }
1071     }
1072 
reportActiveLocked()1073     void reportActiveLocked() {
1074         // active is true if pending queue contains jobs OR some job is running.
1075         boolean active = mPendingJobs.size() > 0;
1076         if (mPendingJobs.size() <= 0) {
1077             for (int i=0; i<mActiveServices.size(); i++) {
1078                 final JobServiceContext jsc = mActiveServices.get(i);
1079                 final JobStatus job = jsc.getRunningJobLocked();
1080                 if (job != null
1081                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
1082                         && !job.dozeWhitelisted
1083                         && !job.uidActive) {
1084                     // We will report active if we have a job running and it is not an exception
1085                     // due to being in the foreground or whitelisted.
1086                     active = true;
1087                     break;
1088                 }
1089             }
1090         }
1091 
1092         if (mReportedActive != active) {
1093             mReportedActive = active;
1094             if (mLocalDeviceIdleController != null) {
1095                 mLocalDeviceIdleController.setJobsActive(active);
1096             }
1097         }
1098     }
1099 
reportAppUsage(String packageName, int userId)1100     void reportAppUsage(String packageName, int userId) {
1101         // This app just transitioned into interactive use or near equivalent, so we should
1102         // take a look at its job state for feedback purposes.
1103     }
1104 
1105     /**
1106      * Initializes the system service.
1107      * <p>
1108      * Subclasses must define a single argument constructor that accepts the context
1109      * and passes it to super.
1110      * </p>
1111      *
1112      * @param context The system server context.
1113      */
JobSchedulerService(Context context)1114     public JobSchedulerService(Context context) {
1115         super(context);
1116 
1117         mLocalPM = LocalServices.getService(PackageManagerInternal.class);
1118         mActivityManagerInternal = Preconditions.checkNotNull(
1119                 LocalServices.getService(ActivityManagerInternal.class));
1120 
1121         mHandler = new JobHandler(context.getMainLooper());
1122         mConstants = new Constants();
1123         mConstantsObserver = new ConstantsObserver(mHandler);
1124         mJobSchedulerStub = new JobSchedulerStub();
1125 
1126         // Set up the app standby bucketing tracker
1127         mStandbyTracker = new StandbyTracker();
1128         mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1129         mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
1130 
1131         // The job store needs to call back
1132         publishLocalService(JobSchedulerInternal.class, new LocalService());
1133 
1134         // Initialize the job store and set up any persisted jobs
1135         mJobs = JobStore.initAndGet(this);
1136 
1137         // Create the controllers.
1138         mControllers = new ArrayList<StateController>();
1139         mControllers.add(new ConnectivityController(this));
1140         mControllers.add(new TimeController(this));
1141         mControllers.add(new IdleController(this));
1142         mBatteryController = new BatteryController(this);
1143         mControllers.add(mBatteryController);
1144         mStorageController = new StorageController(this);
1145         mControllers.add(mStorageController);
1146         mControllers.add(new BackgroundJobsController(this));
1147         mControllers.add(new ContentObserverController(this));
1148         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
1149         mControllers.add(mDeviceIdleJobsController);
1150 
1151         // If the job store determined that it can't yet reschedule persisted jobs,
1152         // we need to start watching the clock.
1153         if (!mJobs.jobTimesInflatedValid()) {
1154             Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1155             context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1156         }
1157     }
1158 
1159     private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1160         @Override
1161         public void onReceive(Context context, Intent intent) {
1162             if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1163                 // When we reach clock sanity, recalculate the temporal windows
1164                 // of all affected jobs.
1165                 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
1166                     Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1167 
1168                     // We've done our job now, so stop watching the time.
1169                     context.unregisterReceiver(this);
1170 
1171                     // And kick off the work to update the affected jobs, using a secondary
1172                     // thread instead of chugging away here on the main looper thread.
1173                     FgThread.getHandler().post(mJobTimeUpdater);
1174                 }
1175             }
1176         }
1177     };
1178 
1179     private final Runnable mJobTimeUpdater = () -> {
1180         final ArrayList<JobStatus> toRemove = new ArrayList<>();
1181         final ArrayList<JobStatus> toAdd = new ArrayList<>();
1182         synchronized (mLock) {
1183             // Note: we intentionally both look up the existing affected jobs and replace them
1184             // with recalculated ones inside the same lock lifetime.
1185             getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1186 
1187             // Now, at each position [i], we have both the existing JobStatus
1188             // and the one that replaces it.
1189             final int N = toAdd.size();
1190             for (int i = 0; i < N; i++) {
1191                 final JobStatus oldJob = toRemove.get(i);
1192                 final JobStatus newJob = toAdd.get(i);
1193                 if (DEBUG) {
1194                     Slog.v(TAG, "  replacing " + oldJob + " with " + newJob);
1195                 }
1196                 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1197             }
1198         }
1199     };
1200 
1201     @Override
onStart()1202     public void onStart() {
1203         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1204     }
1205 
1206     @Override
onBootPhase(int phase)1207     public void onBootPhase(int phase) {
1208         if (PHASE_SYSTEM_SERVICES_READY == phase) {
1209             mConstantsObserver.start(getContext().getContentResolver());
1210 
1211             mAppStateTracker = Preconditions.checkNotNull(
1212                     LocalServices.getService(AppStateTracker.class));
1213             setNextHeartbeatAlarm();
1214 
1215             // Register br for package removals and user removals.
1216             final IntentFilter filter = new IntentFilter();
1217             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1218             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
1219             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1220             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
1221             filter.addDataScheme("package");
1222             getContext().registerReceiverAsUser(
1223                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1224             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1225             getContext().registerReceiverAsUser(
1226                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
1227             try {
1228                 ActivityManager.getService().registerUidObserver(mUidObserver,
1229                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
1230                         | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1231                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
1232             } catch (RemoteException e) {
1233                 // ignored; both services live in system_server
1234             }
1235             // Remove any jobs that are not associated with any of the current users.
1236             cancelJobsForNonExistentUsers();
1237         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
1238             synchronized (mLock) {
1239                 // Let's go!
1240                 mReadyToRock = true;
1241                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1242                         BatteryStats.SERVICE_NAME));
1243                 mLocalDeviceIdleController
1244                         = LocalServices.getService(DeviceIdleController.LocalService.class);
1245                 // Create the "runners".
1246                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1247                     mActiveServices.add(
1248                             new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
1249                                     getContext().getMainLooper()));
1250                 }
1251                 // Attach jobs to their controllers.
1252                 mJobs.forEachJob((job) -> {
1253                     for (int controller = 0; controller < mControllers.size(); controller++) {
1254                         final StateController sc = mControllers.get(controller);
1255                         sc.maybeStartTrackingJobLocked(job, null);
1256                     }
1257                 });
1258                 // GO GO GO!
1259                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1260             }
1261         }
1262     }
1263 
1264     /**
1265      * Called when we have a job status object that we need to insert in our
1266      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1267      * about.
1268      */
startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob)1269     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1270         if (!jobStatus.isPreparedLocked()) {
1271             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1272         }
1273         jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
1274         final boolean update = mJobs.add(jobStatus);
1275         if (mReadyToRock) {
1276             for (int i = 0; i < mControllers.size(); i++) {
1277                 StateController controller = mControllers.get(i);
1278                 if (update) {
1279                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1280                 }
1281                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1282             }
1283         }
1284     }
1285 
1286     /**
1287      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1288      * object removed.
1289      */
stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean writeBack)1290     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1291             boolean writeBack) {
1292         // Deal with any remaining work items in the old job.
1293         jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
1294 
1295         // Remove from store as well as controllers.
1296         final boolean removed = mJobs.remove(jobStatus, writeBack);
1297         if (removed && mReadyToRock) {
1298             for (int i=0; i<mControllers.size(); i++) {
1299                 StateController controller = mControllers.get(i);
1300                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1301             }
1302         }
1303         return removed;
1304     }
1305 
stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason)1306     private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
1307         for (int i=0; i<mActiveServices.size(); i++) {
1308             JobServiceContext jsc = mActiveServices.get(i);
1309             final JobStatus executing = jsc.getRunningJobLocked();
1310             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1311                 jsc.cancelExecutingJobLocked(reason, debugReason);
1312                 return true;
1313             }
1314         }
1315         return false;
1316     }
1317 
1318     /**
1319      * @param job JobStatus we are querying against.
1320      * @return Whether or not the job represented by the status object is currently being run or
1321      * is pending.
1322      */
isCurrentlyActiveLocked(JobStatus job)1323     private boolean isCurrentlyActiveLocked(JobStatus job) {
1324         for (int i=0; i<mActiveServices.size(); i++) {
1325             JobServiceContext serviceContext = mActiveServices.get(i);
1326             final JobStatus running = serviceContext.getRunningJobLocked();
1327             if (running != null && running.matches(job.getUid(), job.getJobId())) {
1328                 return true;
1329             }
1330         }
1331         return false;
1332     }
1333 
noteJobsPending(List<JobStatus> jobs)1334     void noteJobsPending(List<JobStatus> jobs) {
1335         for (int i = jobs.size() - 1; i >= 0; i--) {
1336             JobStatus job = jobs.get(i);
1337             mJobPackageTracker.notePending(job);
1338         }
1339     }
1340 
noteJobsNonpending(List<JobStatus> jobs)1341     void noteJobsNonpending(List<JobStatus> jobs) {
1342         for (int i = jobs.size() - 1; i >= 0; i--) {
1343             JobStatus job = jobs.get(i);
1344             mJobPackageTracker.noteNonpending(job);
1345         }
1346     }
1347 
1348     /**
1349      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1350      * specify an override deadline on a failed job (the failed job will run even though it's not
1351      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1352      * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1353      *
1354      * @param failureToReschedule Provided job status that we will reschedule.
1355      * @return A newly instantiated JobStatus with the same constraints as the last job except
1356      * with adjusted timing constraints.
1357      *
1358      * @see #maybeQueueReadyJobsForExecutionLocked
1359      */
getRescheduleJobForFailureLocked(JobStatus failureToReschedule)1360     private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1361         final long elapsedNowMillis = sElapsedRealtimeClock.millis();
1362         final JobInfo job = failureToReschedule.getJob();
1363 
1364         final long initialBackoffMillis = job.getInitialBackoffMillis();
1365         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1366         long delayMillis;
1367 
1368         if (failureToReschedule.hasWorkLocked()) {
1369             if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1370                 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1371                         + backoffAttempts + " > work limit "
1372                         + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1373                 return null;
1374             }
1375         } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1376             Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1377                     + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1378             return null;
1379         }
1380 
1381         switch (job.getBackoffPolicy()) {
1382             case JobInfo.BACKOFF_POLICY_LINEAR: {
1383                 long backoff = initialBackoffMillis;
1384                 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1385                     backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1386                 }
1387                 delayMillis = backoff * backoffAttempts;
1388             } break;
1389             default:
1390                 if (DEBUG) {
1391                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1392                 }
1393             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1394                 long backoff = initialBackoffMillis;
1395                 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1396                     backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1397                 }
1398                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1399             } break;
1400         }
1401         delayMillis =
1402                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1403         JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1404                 elapsedNowMillis + delayMillis,
1405                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
1406                 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
1407         for (int ic=0; ic<mControllers.size(); ic++) {
1408             StateController controller = mControllers.get(ic);
1409             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1410         }
1411         return newJob;
1412     }
1413 
1414     /**
1415      * Called after a periodic has executed so we can reschedule it. We take the last execution
1416      * time of the job to be the time of completion (i.e. the time at which this function is
1417      * called).
1418      * <p>This could be inaccurate b/c the job can run for as long as
1419      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1420      * to underscheduling at least, rather than if we had taken the last execution time to be the
1421      * start of the execution.
1422      * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1423      * tracking as though the job were newly-scheduled.
1424      * @return A new job representing the execution criteria for this instantiation of the
1425      * recurring job.
1426      */
getRescheduleJobForPeriodic(JobStatus periodicToReschedule)1427     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1428         final long elapsedNow = sElapsedRealtimeClock.millis();
1429         // Compute how much of the period is remaining.
1430         long runEarly = 0L;
1431 
1432         // If this periodic was rescheduled it won't have a deadline.
1433         if (periodicToReschedule.hasDeadlineConstraint()) {
1434             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1435         }
1436         long flex = periodicToReschedule.getJob().getFlexMillis();
1437         long period = periodicToReschedule.getJob().getIntervalMillis();
1438         long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1439         long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1440 
1441         if (DEBUG) {
1442             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1443                     newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1444         }
1445         return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1446                 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1447                 0 /* backoffAttempt */,
1448                 sSystemClock.millis() /* lastSuccessfulRunTime */,
1449                 periodicToReschedule.getLastFailedRunTime());
1450     }
1451 
1452     /*
1453      * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1454      * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1455      * reboot behavior.
1456      */
heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId)1457     long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
1458         // The furthest back in pre-boot time that we need to bother with
1459         long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1460         boolean cacheHit = false;
1461         synchronized (mLock) {
1462             HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1463             if (jobPackages != null) {
1464                 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1465                 if (cachedValue < Long.MAX_VALUE) {
1466                     cacheHit = true;
1467                     heartbeat = cachedValue;
1468                 }
1469             }
1470             if (!cacheHit) {
1471                 // We haven't seen it yet; ask usage stats about it
1472                 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1473                 if (timeSinceJob < Long.MAX_VALUE) {
1474                     // Usage stats knows about it from before, so calculate back from that
1475                     // and go from there.
1476                     heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1477                 }
1478                 // If usage stats returned its "not found" MAX_VALUE, we still have the
1479                 // negative default 'heartbeat' value we established above
1480                 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1481             }
1482         }
1483         if (DEBUG_STANDBY) {
1484             Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1485                     + packageName + "/" + userId);
1486         }
1487         return heartbeat;
1488     }
1489 
heartbeatWhenJobsLastRun(JobStatus job)1490     long heartbeatWhenJobsLastRun(JobStatus job) {
1491         return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1492     }
1493 
setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat)1494     void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1495         HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1496         if (jobPackages == null) {
1497             jobPackages = new HashMap<>();
1498             mLastJobHeartbeats.put(userId, jobPackages);
1499         }
1500         jobPackages.put(packageName, heartbeat);
1501     }
1502 
1503     // JobCompletedListener implementations.
1504 
1505     /**
1506      * A job just finished executing. We fetch the
1507      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1508      * whether we want to reschedule we re-add it to the controllers.
1509      * @param jobStatus Completed job.
1510      * @param needsReschedule Whether the implementing class should reschedule this job.
1511      */
1512     @Override
onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule)1513     public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1514         if (DEBUG) {
1515             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1516         }
1517 
1518         // If the job wants to be rescheduled, we first need to make the next upcoming
1519         // job so we can transfer any appropriate state over from the previous job when
1520         // we stop it.
1521         final JobStatus rescheduledJob = needsReschedule
1522                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1523 
1524         // Do not write back immediately if this is a periodic job. The job may get lost if system
1525         // shuts down before it is added back.
1526         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1527             if (DEBUG) {
1528                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1529             }
1530             // We still want to check for jobs to execute, because this job may have
1531             // scheduled a new job under the same job id, and now we can run it.
1532             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1533             return;
1534         }
1535 
1536         if (rescheduledJob != null) {
1537             try {
1538                 rescheduledJob.prepareLocked(ActivityManager.getService());
1539             } catch (SecurityException e) {
1540                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1541             }
1542             startTrackingJobLocked(rescheduledJob, jobStatus);
1543         } else if (jobStatus.getJob().isPeriodic()) {
1544             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1545             try {
1546                 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1547             } catch (SecurityException e) {
1548                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1549             }
1550             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1551         }
1552         jobStatus.unprepareLocked(ActivityManager.getService());
1553         reportActiveLocked();
1554         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1555     }
1556 
1557     // StateChangedListener implementations.
1558 
1559     /**
1560      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1561      * some controller's state has changed, so as to run through the list of jobs and start/stop
1562      * any that are eligible.
1563      */
1564     @Override
onControllerStateChanged()1565     public void onControllerStateChanged() {
1566         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1567     }
1568 
1569     @Override
onRunJobNow(JobStatus jobStatus)1570     public void onRunJobNow(JobStatus jobStatus) {
1571         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1572     }
1573 
1574     final private class JobHandler extends Handler {
1575 
JobHandler(Looper looper)1576         public JobHandler(Looper looper) {
1577             super(looper);
1578         }
1579 
1580         @Override
handleMessage(Message message)1581         public void handleMessage(Message message) {
1582             synchronized (mLock) {
1583                 if (!mReadyToRock) {
1584                     return;
1585                 }
1586                 switch (message.what) {
1587                     case MSG_JOB_EXPIRED: {
1588                         JobStatus runNow = (JobStatus) message.obj;
1589                         // runNow can be null, which is a controller's way of indicating that its
1590                         // state is such that all ready jobs should be run immediately.
1591                         if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
1592                             mJobPackageTracker.notePending(runNow);
1593                             addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
1594                         } else {
1595                             queueReadyJobsForExecutionLocked();
1596                         }
1597                     } break;
1598                     case MSG_CHECK_JOB:
1599                         if (mReportedActive) {
1600                             // if jobs are currently being run, queue all ready jobs for execution.
1601                             queueReadyJobsForExecutionLocked();
1602                         } else {
1603                             // Check the list of jobs and run some of them if we feel inclined.
1604                             maybeQueueReadyJobsForExecutionLocked();
1605                         }
1606                         break;
1607                     case MSG_CHECK_JOB_GREEDY:
1608                         queueReadyJobsForExecutionLocked();
1609                         break;
1610                     case MSG_STOP_JOB:
1611                         cancelJobImplLocked((JobStatus) message.obj, null,
1612                                 "app no longer allowed to run");
1613                         break;
1614 
1615                     case MSG_UID_STATE_CHANGED: {
1616                         final int uid = message.arg1;
1617                         final int procState = message.arg2;
1618                         updateUidState(uid, procState);
1619                         break;
1620                     }
1621                     case MSG_UID_GONE: {
1622                         final int uid = message.arg1;
1623                         final boolean disabled = message.arg2 != 0;
1624                         updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1625                         if (disabled) {
1626                             cancelJobsForUid(uid, "uid gone");
1627                         }
1628                         synchronized (mLock) {
1629                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1630                         }
1631                         break;
1632                     }
1633                     case MSG_UID_ACTIVE: {
1634                         final int uid = message.arg1;
1635                         synchronized (mLock) {
1636                             mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1637                         }
1638                         break;
1639                     }
1640                     case MSG_UID_IDLE: {
1641                         final int uid = message.arg1;
1642                         final boolean disabled = message.arg2 != 0;
1643                         if (disabled) {
1644                             cancelJobsForUid(uid, "app uid idle");
1645                         }
1646                         synchronized (mLock) {
1647                             mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1648                         }
1649                         break;
1650                     }
1651 
1652                 }
1653                 maybeRunPendingJobsLocked();
1654                 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1655                 removeMessages(MSG_CHECK_JOB);
1656             }
1657         }
1658     }
1659 
stopNonReadyActiveJobsLocked()1660     private void stopNonReadyActiveJobsLocked() {
1661         for (int i=0; i<mActiveServices.size(); i++) {
1662             JobServiceContext serviceContext = mActiveServices.get(i);
1663             final JobStatus running = serviceContext.getRunningJobLocked();
1664             if (running != null && !running.isReady()) {
1665                 serviceContext.cancelExecutingJobLocked(
1666                         JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1667                         "cancelled due to unsatisfied constraints");
1668             }
1669         }
1670     }
1671 
1672     /**
1673      * Run through list of jobs and execute all possible - at least one is expired so we do
1674      * as many as we can.
1675      */
queueReadyJobsForExecutionLocked()1676     private void queueReadyJobsForExecutionLocked() {
1677         if (DEBUG) {
1678             Slog.d(TAG, "queuing all ready jobs for execution:");
1679         }
1680         noteJobsNonpending(mPendingJobs);
1681         mPendingJobs.clear();
1682         stopNonReadyActiveJobsLocked();
1683         mJobs.forEachJob(mReadyQueueFunctor);
1684         mReadyQueueFunctor.postProcess();
1685 
1686         if (DEBUG) {
1687             final int queuedJobs = mPendingJobs.size();
1688             if (queuedJobs == 0) {
1689                 Slog.d(TAG, "No jobs pending.");
1690             } else {
1691                 Slog.d(TAG, queuedJobs + " jobs queued.");
1692             }
1693         }
1694     }
1695 
1696     final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
1697         ArrayList<JobStatus> newReadyJobs;
1698 
1699         @Override
accept(JobStatus job)1700         public void accept(JobStatus job) {
1701             if (isReadyToBeExecutedLocked(job)) {
1702                 if (DEBUG) {
1703                     Slog.d(TAG, "    queued " + job.toShortString());
1704                 }
1705                 if (newReadyJobs == null) {
1706                     newReadyJobs = new ArrayList<JobStatus>();
1707                 }
1708                 newReadyJobs.add(job);
1709             }
1710         }
1711 
postProcess()1712         public void postProcess() {
1713             if (newReadyJobs != null) {
1714                 noteJobsPending(newReadyJobs);
1715                 mPendingJobs.addAll(newReadyJobs);
1716                 if (mPendingJobs.size() > 1) {
1717                     mPendingJobs.sort(mEnqueueTimeComparator);
1718                 }
1719             }
1720             newReadyJobs = null;
1721         }
1722     }
1723     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1724 
1725     /**
1726      * The state of at least one job has changed. Here is where we could enforce various
1727      * policies on when we want to execute jobs.
1728      * Right now the policy is such:
1729      * If >1 of the ready jobs is idle mode we send all of them off
1730      * if more than 2 network connectivity jobs are ready we send them all off.
1731      * If more than 4 jobs total are ready we send them all off.
1732      * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1733      */
1734     final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
1735         int chargingCount;
1736         int batteryNotLowCount;
1737         int storageNotLowCount;
1738         int idleCount;
1739         int backoffCount;
1740         int connectivityCount;
1741         int contentCount;
1742         List<JobStatus> runnableJobs;
1743 
MaybeReadyJobQueueFunctor()1744         public MaybeReadyJobQueueFunctor() {
1745             reset();
1746         }
1747 
1748         // Functor method invoked for each job via JobStore.forEachJob()
1749         @Override
accept(JobStatus job)1750         public void accept(JobStatus job) {
1751             if (isReadyToBeExecutedLocked(job)) {
1752                 try {
1753                     if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1754                             job.getJob().getService().getPackageName())) {
1755                         Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1756                                 + job.getJob().toString() + " -- package not allowed to start");
1757                         mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1758                         return;
1759                     }
1760                 } catch (RemoteException e) {
1761                 }
1762                 if (job.getNumFailures() > 0) {
1763                     backoffCount++;
1764                 }
1765                 if (job.hasIdleConstraint()) {
1766                     idleCount++;
1767                 }
1768                 if (job.hasConnectivityConstraint()) {
1769                     connectivityCount++;
1770                 }
1771                 if (job.hasChargingConstraint()) {
1772                     chargingCount++;
1773                 }
1774                 if (job.hasBatteryNotLowConstraint()) {
1775                     batteryNotLowCount++;
1776                 }
1777                 if (job.hasStorageNotLowConstraint()) {
1778                     storageNotLowCount++;
1779                 }
1780                 if (job.hasContentTriggerConstraint()) {
1781                     contentCount++;
1782                 }
1783                 if (runnableJobs == null) {
1784                     runnableJobs = new ArrayList<>();
1785                 }
1786                 runnableJobs.add(job);
1787             }
1788         }
1789 
postProcess()1790         public void postProcess() {
1791             if (backoffCount > 0 ||
1792                     idleCount >= mConstants.MIN_IDLE_COUNT ||
1793                     connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1794                     chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1795                     batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1796                     storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1797                     contentCount >= mConstants.MIN_CONTENT_COUNT ||
1798                     (runnableJobs != null
1799                             && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1800                 if (DEBUG) {
1801                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1802                 }
1803                 noteJobsPending(runnableJobs);
1804                 mPendingJobs.addAll(runnableJobs);
1805                 if (mPendingJobs.size() > 1) {
1806                     mPendingJobs.sort(mEnqueueTimeComparator);
1807                 }
1808             } else {
1809                 if (DEBUG) {
1810                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1811                 }
1812             }
1813 
1814             // Be ready for next time
1815             reset();
1816         }
1817 
reset()1818         private void reset() {
1819             chargingCount = 0;
1820             idleCount =  0;
1821             backoffCount = 0;
1822             connectivityCount = 0;
1823             batteryNotLowCount = 0;
1824             storageNotLowCount = 0;
1825             contentCount = 0;
1826             runnableJobs = null;
1827         }
1828     }
1829     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1830 
maybeQueueReadyJobsForExecutionLocked()1831     private void maybeQueueReadyJobsForExecutionLocked() {
1832         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1833 
1834         noteJobsNonpending(mPendingJobs);
1835         mPendingJobs.clear();
1836         stopNonReadyActiveJobsLocked();
1837         mJobs.forEachJob(mMaybeQueueFunctor);
1838         mMaybeQueueFunctor.postProcess();
1839     }
1840 
1841     /**
1842      * Heartbeat tracking.  The heartbeat alarm is intentionally non-wakeup.
1843      */
1844     class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1845 
1846         @Override
onAlarm()1847         public void onAlarm() {
1848             synchronized (mLock) {
1849                 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1850                 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1851                 if (beatsElapsed > 0) {
1852                     mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1853                     advanceHeartbeatLocked(beatsElapsed);
1854                 }
1855             }
1856             setNextHeartbeatAlarm();
1857         }
1858     }
1859 
1860     // Intentionally does not touch the alarm timing
advanceHeartbeatLocked(long beatsElapsed)1861     void advanceHeartbeatLocked(long beatsElapsed) {
1862         mHeartbeat += beatsElapsed;
1863         if (DEBUG_STANDBY) {
1864             Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1865                     + " to " + mHeartbeat);
1866         }
1867         // Don't update ACTIVE or NEVER bucket milestones.  Note that mHeartbeat
1868         // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1869         // new jobs scheduled by apps in that bucket will be permitted to run
1870         // immediately.
1871         boolean didAdvanceBucket = false;
1872         for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
1873             // Did we reach or cross a bucket boundary?
1874             if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1875                 didAdvanceBucket = true;
1876             }
1877             while (mHeartbeat > mNextBucketHeartbeat[i]) {
1878                 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1879             }
1880             if (DEBUG_STANDBY) {
1881                 Slog.v(TAG, "   Bucket " + i + " next heartbeat "
1882                         + mNextBucketHeartbeat[i]);
1883             }
1884         }
1885 
1886         if (didAdvanceBucket) {
1887             if (DEBUG_STANDBY) {
1888                 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1889             }
1890             mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1891         }
1892     }
1893 
setNextHeartbeatAlarm()1894     void setNextHeartbeatAlarm() {
1895         final long heartbeatLength;
1896         synchronized (mLock) {
1897             heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1898         }
1899         final long now = sElapsedRealtimeClock.millis();
1900         final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1901         final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1902         if (DEBUG_STANDBY) {
1903             Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1904                     + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1905         }
1906         AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1907         am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1908                 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
1909     }
1910 
1911     /**
1912      * Criteria for moving a job into the pending queue:
1913      *      - It's ready.
1914      *      - It's not pending.
1915      *      - It's not already running on a JSC.
1916      *      - The user that requested the job is running.
1917      *      - The job's standby bucket has come due to be runnable.
1918      *      - The component is enabled and runnable.
1919      */
isReadyToBeExecutedLocked(JobStatus job)1920     private boolean isReadyToBeExecutedLocked(JobStatus job) {
1921         final boolean jobReady = job.isReady();
1922 
1923         if (DEBUG) {
1924             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1925                     + " ready=" + jobReady);
1926         }
1927 
1928         // This is a condition that is very likely to be false (most jobs that are
1929         // scheduled are sitting there, not ready yet) and very cheap to check (just
1930         // a few conditions on data in JobStatus).
1931         if (!jobReady) {
1932             if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
1933                 Slog.v(TAG, "    NOT READY: " + job);
1934             }
1935             return false;
1936         }
1937 
1938         final boolean jobExists = mJobs.containsJob(job);
1939 
1940         final int userId = job.getUserId();
1941         final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1942 
1943         if (DEBUG) {
1944             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1945                     + " exists=" + jobExists + " userStarted=" + userStarted);
1946         }
1947 
1948         // These are also fairly cheap to check, though they typically will not
1949         // be conditions we fail.
1950         if (!jobExists || !userStarted) {
1951             return false;
1952         }
1953 
1954         final boolean jobPending = mPendingJobs.contains(job);
1955         final boolean jobActive = isCurrentlyActiveLocked(job);
1956 
1957         if (DEBUG) {
1958             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1959                     + " pending=" + jobPending + " active=" + jobActive);
1960         }
1961 
1962         // These can be a little more expensive (especially jobActive, since we need to
1963         // go through the array of all potentially active jobs), so we are doing them
1964         // later...  but still before checking with the package manager!
1965         if (jobPending || jobActive) {
1966             return false;
1967         }
1968 
1969         // If the app is in a non-active standby bucket, make sure we've waited
1970         // an appropriate amount of time since the last invocation.  During device-
1971         // wide parole, standby bucketing is ignored.
1972         //
1973         // Jobs in 'active' apps are not subject to standby, nor are jobs that are
1974         // specifically marked as exempt.
1975         if (DEBUG_STANDBY) {
1976             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1977                     + " parole=" + mInParole + " active=" + job.uidActive
1978                     + " exempt=" + job.getJob().isExemptedFromAppStandby());
1979         }
1980         if (!mInParole
1981                 && !job.uidActive
1982                 && !job.getJob().isExemptedFromAppStandby()) {
1983             final int bucket = job.getStandbyBucket();
1984             if (DEBUG_STANDBY) {
1985                 Slog.v(TAG, "  bucket=" + bucket + " heartbeat=" + mHeartbeat
1986                         + " next=" + mNextBucketHeartbeat[bucket]);
1987             }
1988             if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
1989                 // Only skip this job if the app is still waiting for the end of its nominal
1990                 // bucket interval.  Once it's waited that long, we let it go ahead and clear.
1991                 // The final (NEVER) bucket is special; we never age those apps' jobs into
1992                 // runnability.
1993                 final long appLastRan = heartbeatWhenJobsLastRun(job);
1994                 if (bucket >= mConstants.STANDBY_BEATS.length
1995                         || (mHeartbeat > appLastRan
1996                                 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
1997                     // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1998                     if (job.getWhenStandbyDeferred() == 0) {
1999                         if (DEBUG_STANDBY) {
2000                             Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
2001                                     + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2002                                     + " for " + job);
2003                         }
2004                         job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
2005                     }
2006                     return false;
2007                 } else {
2008                     if (DEBUG_STANDBY) {
2009                         Slog.v(TAG, "Bucket deferred job aged into runnability at "
2010                                 + mHeartbeat + " : " + job);
2011                     }
2012                 }
2013             }
2014         }
2015 
2016         // The expensive check last: validate that the defined package+service is
2017         // still present & viable.
2018         final boolean componentPresent;
2019         try {
2020             componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2021                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2022                     userId) != null);
2023         } catch (RemoteException e) {
2024             throw e.rethrowAsRuntimeException();
2025         }
2026 
2027         if (DEBUG) {
2028             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2029                     + " componentPresent=" + componentPresent);
2030         }
2031 
2032         // Everything else checked out so far, so this is the final yes/no check
2033         return componentPresent;
2034     }
2035 
2036     /**
2037      * Reconcile jobs in the pending queue against available execution contexts.
2038      * A controller can force a job into the pending queue even if it's already running, but
2039      * here is where we decide whether to actually execute it.
2040      */
maybeRunPendingJobsLocked()2041     private void maybeRunPendingJobsLocked() {
2042         if (DEBUG) {
2043             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2044         }
2045         assignJobsToContextsLocked();
2046         reportActiveLocked();
2047     }
2048 
adjustJobPriority(int curPriority, JobStatus job)2049     private int adjustJobPriority(int curPriority, JobStatus job) {
2050         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2051             float factor = mJobPackageTracker.getLoadFactor(job);
2052             if (factor >= mConstants.HEAVY_USE_FACTOR) {
2053                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
2054             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
2055                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2056             }
2057         }
2058         return curPriority;
2059     }
2060 
evaluateJobPriorityLocked(JobStatus job)2061     private int evaluateJobPriorityLocked(JobStatus job) {
2062         int priority = job.getPriority();
2063         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
2064             return adjustJobPriority(priority, job);
2065         }
2066         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2067         if (override != 0) {
2068             return adjustJobPriority(override, job);
2069         }
2070         return adjustJobPriority(priority, job);
2071     }
2072 
2073     /**
2074      * Takes jobs from pending queue and runs them on available contexts.
2075      * If no contexts are available, preempts lower priority jobs to
2076      * run higher priority ones.
2077      * Lock on mJobs before calling this function.
2078      */
assignJobsToContextsLocked()2079     private void assignJobsToContextsLocked() {
2080         if (DEBUG) {
2081             Slog.d(TAG, printPendingQueue());
2082         }
2083 
2084         int memLevel;
2085         try {
2086             memLevel = ActivityManager.getService().getMemoryTrimLevel();
2087         } catch (RemoteException e) {
2088             memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
2089         }
2090         switch (memLevel) {
2091             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
2092                 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
2093                 break;
2094             case ProcessStats.ADJ_MEM_FACTOR_LOW:
2095                 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
2096                 break;
2097             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
2098                 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
2099                 break;
2100             default:
2101                 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
2102                 break;
2103         }
2104 
2105         JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
2106         boolean[] act = mTmpAssignAct;
2107         int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2108         int numActive = 0;
2109         int numForeground = 0;
2110         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2111             final JobServiceContext js = mActiveServices.get(i);
2112             final JobStatus status = js.getRunningJobLocked();
2113             if ((contextIdToJobMap[i] = status) != null) {
2114                 numActive++;
2115                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2116                     numForeground++;
2117                 }
2118             }
2119             act[i] = false;
2120             preferredUidForContext[i] = js.getPreferredUid();
2121         }
2122         if (DEBUG) {
2123             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2124         }
2125         for (int i=0; i<mPendingJobs.size(); i++) {
2126             JobStatus nextPending = mPendingJobs.get(i);
2127 
2128             // If job is already running, go to next job.
2129             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2130             if (jobRunningContext != -1) {
2131                 continue;
2132             }
2133 
2134             final int priority = evaluateJobPriorityLocked(nextPending);
2135             nextPending.lastEvaluatedPriority = priority;
2136 
2137             // Find a context for nextPending. The context should be available OR
2138             // it should have lowest priority among all running jobs
2139             // (sharing the same Uid as nextPending)
2140             int minPriority = Integer.MAX_VALUE;
2141             int minPriorityContextId = -1;
2142             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2143                 JobStatus job = contextIdToJobMap[j];
2144                 int preferredUid = preferredUidForContext[j];
2145                 if (job == null) {
2146                     if ((numActive < mMaxActiveJobs ||
2147                             (priority >= JobInfo.PRIORITY_TOP_APP &&
2148                                     numForeground < mConstants.FG_JOB_COUNT)) &&
2149                             (preferredUid == nextPending.getUid() ||
2150                                     preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2151                         // This slot is free, and we haven't yet hit the limit on
2152                         // concurrent jobs...  we can just throw the job in to here.
2153                         minPriorityContextId = j;
2154                         break;
2155                     }
2156                     // No job on this context, but nextPending can't run here because
2157                     // the context has a preferred Uid or we have reached the limit on
2158                     // concurrent jobs.
2159                     continue;
2160                 }
2161                 if (job.getUid() != nextPending.getUid()) {
2162                     continue;
2163                 }
2164                 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
2165                     continue;
2166                 }
2167                 if (minPriority > nextPending.lastEvaluatedPriority) {
2168                     minPriority = nextPending.lastEvaluatedPriority;
2169                     minPriorityContextId = j;
2170                 }
2171             }
2172             if (minPriorityContextId != -1) {
2173                 contextIdToJobMap[minPriorityContextId] = nextPending;
2174                 act[minPriorityContextId] = true;
2175                 numActive++;
2176                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2177                     numForeground++;
2178                 }
2179             }
2180         }
2181         if (DEBUG) {
2182             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2183         }
2184         mJobPackageTracker.noteConcurrency(numActive, numForeground);
2185         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2186             boolean preservePreferredUid = false;
2187             if (act[i]) {
2188                 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
2189                 if (js != null) {
2190                     if (DEBUG) {
2191                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
2192                     }
2193                     // preferredUid will be set to uid of currently running job.
2194                     mActiveServices.get(i).preemptExecutingJobLocked();
2195                     preservePreferredUid = true;
2196                 } else {
2197                     final JobStatus pendingJob = contextIdToJobMap[i];
2198                     if (DEBUG) {
2199                         Slog.d(TAG, "About to run job on context "
2200                                 + String.valueOf(i) + ", job: " + pendingJob);
2201                     }
2202                     for (int ic=0; ic<mControllers.size(); ic++) {
2203                         mControllers.get(ic).prepareForExecutionLocked(pendingJob);
2204                     }
2205                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2206                         Slog.d(TAG, "Error executing " + pendingJob);
2207                     }
2208                     if (mPendingJobs.remove(pendingJob)) {
2209                         mJobPackageTracker.noteNonpending(pendingJob);
2210                     }
2211                 }
2212             }
2213             if (!preservePreferredUid) {
2214                 mActiveServices.get(i).clearPreferredUid();
2215             }
2216         }
2217     }
2218 
findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map)2219     int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2220         for (int i=0; i<map.length; i++) {
2221             if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2222                 return i;
2223             }
2224         }
2225         return -1;
2226     }
2227 
2228     final class LocalService implements JobSchedulerInternal {
2229 
2230         /**
2231          * The current bucket heartbeat ordinal
2232          */
currentHeartbeat()2233         public long currentHeartbeat() {
2234             return getCurrentHeartbeat();
2235         }
2236 
2237         /**
2238          * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2239          */
nextHeartbeatForBucket(int bucket)2240         public long nextHeartbeatForBucket(int bucket) {
2241             synchronized (mLock) {
2242                 return mNextBucketHeartbeat[bucket];
2243             }
2244         }
2245 
2246         /**
2247          * Heartbeat ordinal for the given app.  This is typically the heartbeat at which
2248          * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2249          * jobs in a long time is immediately runnable even if the app is bucketed into
2250          * an infrequent time allocation.
2251          */
baseHeartbeatForApp(String packageName, @UserIdInt int userId, final int appStandbyBucket)2252         public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2253                 final int appStandbyBucket) {
2254             if (appStandbyBucket == 0 ||
2255                     appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2256                 // ACTIVE => everything can be run right away
2257                 // NEVER => we won't run them anyway, so let them go in the future
2258                 // as soon as the app enters normal use
2259                 if (DEBUG_STANDBY) {
2260                     Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2261                             + packageName + "/" + userId);
2262                 }
2263                 return 0;
2264             }
2265 
2266             final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2267             if (DEBUG_STANDBY) {
2268                 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2269                         + packageName + "/" + userId);
2270             }
2271             return baseHeartbeat;
2272         }
2273 
noteJobStart(String packageName, int userId)2274         public void noteJobStart(String packageName, int userId) {
2275             synchronized (mLock) {
2276                 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2277             }
2278         }
2279 
2280         /**
2281          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2282          * jobs are always considered pending.
2283          */
2284         @Override
getSystemScheduledPendingJobs()2285         public List<JobInfo> getSystemScheduledPendingJobs() {
2286             synchronized (mLock) {
2287                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
2288                 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2289                     if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2290                         pendingJobs.add(job.getJob());
2291                     }
2292                 });
2293                 return pendingJobs;
2294             }
2295         }
2296 
2297         @Override
cancelJobsForUid(int uid, String reason)2298         public void cancelJobsForUid(int uid, String reason) {
2299             JobSchedulerService.this.cancelJobsForUid(uid, reason);
2300         }
2301 
2302         @Override
addBackingUpUid(int uid)2303         public void addBackingUpUid(int uid) {
2304             synchronized (mLock) {
2305                 // No need to actually do anything here, since for a full backup the
2306                 // activity manager will kill the process which will kill the job (and
2307                 // cause it to restart, but now it can't run).
2308                 mBackingUpUids.put(uid, uid);
2309             }
2310         }
2311 
2312         @Override
removeBackingUpUid(int uid)2313         public void removeBackingUpUid(int uid) {
2314             synchronized (mLock) {
2315                 mBackingUpUids.delete(uid);
2316                 // If there are any jobs for this uid, we need to rebuild the pending list
2317                 // in case they are now ready to run.
2318                 if (mJobs.countJobsForUid(uid) > 0) {
2319                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2320                 }
2321             }
2322         }
2323 
2324         @Override
clearAllBackingUpUids()2325         public void clearAllBackingUpUids() {
2326             synchronized (mLock) {
2327                 if (mBackingUpUids.size() > 0) {
2328                     mBackingUpUids.clear();
2329                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2330                 }
2331             }
2332         }
2333 
2334         @Override
reportAppUsage(String packageName, int userId)2335         public void reportAppUsage(String packageName, int userId) {
2336             JobSchedulerService.this.reportAppUsage(packageName, userId);
2337         }
2338 
2339         @Override
getPersistStats()2340         public JobStorePersistStats getPersistStats() {
2341             synchronized (mLock) {
2342                 return new JobStorePersistStats(mJobs.getPersistStats());
2343             }
2344         }
2345     }
2346 
2347     /**
2348      * Tracking of app assignments to standby buckets
2349      */
2350     final class StandbyTracker extends AppIdleStateChangeListener {
2351 
2352         // AppIdleStateChangeListener interface for live updates
2353 
2354         @Override
onAppIdleStateChanged(final String packageName, final @UserIdInt int userId, boolean idle, int bucket, int reason)2355         public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
2356                 boolean idle, int bucket, int reason) {
2357             final int uid = mLocalPM.getPackageUid(packageName,
2358                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2359             if (uid < 0) {
2360                 if (DEBUG_STANDBY) {
2361                     Slog.i(TAG, "App idle state change for unknown app "
2362                             + packageName + "/" + userId);
2363                 }
2364                 return;
2365             }
2366 
2367             final int bucketIndex = standbyBucketToBucketIndex(bucket);
2368             // update job bookkeeping out of band
2369             BackgroundThread.getHandler().post(() -> {
2370                 if (DEBUG_STANDBY) {
2371                     Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2372                 }
2373                 synchronized (mLock) {
2374                     mJobs.forEachJobForSourceUid(uid, job -> {
2375                         // double-check uid vs package name to disambiguate shared uids
2376                         if (packageName.equals(job.getSourcePackageName())) {
2377                             job.setStandbyBucket(bucketIndex);
2378                         }
2379                     });
2380                     onControllerStateChanged();
2381                 }
2382             });
2383         }
2384 
2385         @Override
onParoleStateChanged(boolean isParoleOn)2386         public void onParoleStateChanged(boolean isParoleOn) {
2387             if (DEBUG_STANDBY) {
2388                 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2389             }
2390             mInParole = isParoleOn;
2391         }
2392 
2393         @Override
onUserInteractionStarted(String packageName, int userId)2394         public void onUserInteractionStarted(String packageName, int userId) {
2395             final int uid = mLocalPM.getPackageUid(packageName,
2396                     PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2397             if (uid < 0) {
2398                 // Quietly ignore; the case is already logged elsewhere
2399                 return;
2400             }
2401 
2402             long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2403             if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2404                 // Too long ago, not worth logging
2405                 sinceLast = 0L;
2406             }
2407             final DeferredJobCounter counter = new DeferredJobCounter();
2408             synchronized (mLock) {
2409                 mJobs.forEachJobForSourceUid(uid, counter);
2410             }
2411             if (counter.numDeferred() > 0 || sinceLast > 0) {
2412                 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2413                         (BatteryStatsInternal.class);
2414                 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2415             }
2416         }
2417     }
2418 
2419     static class DeferredJobCounter implements Consumer<JobStatus> {
2420         private int mDeferred = 0;
2421 
numDeferred()2422         public int numDeferred() {
2423             return mDeferred;
2424         }
2425 
2426         @Override
accept(JobStatus job)2427         public void accept(JobStatus job) {
2428             if (job.getWhenStandbyDeferred() > 0) {
2429                 mDeferred++;
2430             }
2431         }
2432     }
2433 
standbyBucketToBucketIndex(int bucket)2434     public static int standbyBucketToBucketIndex(int bucket) {
2435         // Normalize AppStandby constants to indices into our bookkeeping
2436         if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2437         else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2438         else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2439         else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2440         else return ACTIVE_INDEX;
2441     }
2442 
2443     // Static to support external callers
standbyBucketForPackage(String packageName, int userId, long elapsedNow)2444     public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2445         UsageStatsManagerInternal usageStats = LocalServices.getService(
2446                 UsageStatsManagerInternal.class);
2447         int bucket = usageStats != null
2448                 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2449                 : 0;
2450 
2451         bucket = standbyBucketToBucketIndex(bucket);
2452 
2453         if (DEBUG_STANDBY) {
2454             Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2455         }
2456         return bucket;
2457     }
2458 
2459     /**
2460      * Binder stub trampoline implementation
2461      */
2462     final class JobSchedulerStub extends IJobScheduler.Stub {
2463         /** Cache determination of whether a given app can persist jobs
2464          * key is uid of the calling app; value is undetermined/true/false
2465          */
2466         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2467 
2468         // Enforce that only the app itself (or shared uid participant) can schedule a
2469         // job that runs one of the app's services, as well as verifying that the
2470         // named service properly requires the BIND_JOB_SERVICE permission
enforceValidJobRequest(int uid, JobInfo job)2471         private void enforceValidJobRequest(int uid, JobInfo job) {
2472             final IPackageManager pm = AppGlobals.getPackageManager();
2473             final ComponentName service = job.getService();
2474             try {
2475                 ServiceInfo si = pm.getServiceInfo(service,
2476                         PackageManager.MATCH_DIRECT_BOOT_AWARE
2477                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
2478                         UserHandle.getUserId(uid));
2479                 if (si == null) {
2480                     throw new IllegalArgumentException("No such service " + service);
2481                 }
2482                 if (si.applicationInfo.uid != uid) {
2483                     throw new IllegalArgumentException("uid " + uid +
2484                             " cannot schedule job in " + service.getPackageName());
2485                 }
2486                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2487                     throw new IllegalArgumentException("Scheduled service " + service
2488                             + " does not require android.permission.BIND_JOB_SERVICE permission");
2489                 }
2490             } catch (RemoteException e) {
2491                 // Can't happen; the Package Manager is in this same process
2492             }
2493         }
2494 
canPersistJobs(int pid, int uid)2495         private boolean canPersistJobs(int pid, int uid) {
2496             // If we get this far we're good to go; all we need to do now is check
2497             // whether the app is allowed to persist its scheduled work.
2498             final boolean canPersist;
2499             synchronized (mPersistCache) {
2500                 Boolean cached = mPersistCache.get(uid);
2501                 if (cached != null) {
2502                     canPersist = cached.booleanValue();
2503                 } else {
2504                     // Persisting jobs is tantamount to running at boot, so we permit
2505                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2506                     // permission
2507                     int result = getContext().checkPermission(
2508                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2509                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
2510                     mPersistCache.put(uid, canPersist);
2511                 }
2512             }
2513             return canPersist;
2514         }
2515 
validateJobFlags(JobInfo job, int callingUid)2516         private void validateJobFlags(JobInfo job, int callingUid) {
2517             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2518                 getContext().enforceCallingOrSelfPermission(
2519                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2520             }
2521             if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2522                 if (callingUid != Process.SYSTEM_UID) {
2523                     throw new SecurityException("Job has invalid flags");
2524                 }
2525                 if (job.isPeriodic()) {
2526                     Slog.wtf(TAG, "Periodic jobs mustn't have"
2527                             + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
2528                 }
2529             }
2530         }
2531 
2532         // IJobScheduler implementation
2533         @Override
schedule(JobInfo job)2534         public int schedule(JobInfo job) throws RemoteException {
2535             if (DEBUG) {
2536                 Slog.d(TAG, "Scheduling job: " + job.toString());
2537             }
2538             final int pid = Binder.getCallingPid();
2539             final int uid = Binder.getCallingUid();
2540             final int userId = UserHandle.getUserId(uid);
2541 
2542             enforceValidJobRequest(uid, job);
2543             if (job.isPersisted()) {
2544                 if (!canPersistJobs(pid, uid)) {
2545                     throw new IllegalArgumentException("Error: requested job be persisted without"
2546                             + " holding RECEIVE_BOOT_COMPLETED permission.");
2547                 }
2548             }
2549 
2550             validateJobFlags(job, uid);
2551 
2552             long ident = Binder.clearCallingIdentity();
2553             try {
2554                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2555                         null);
2556             } finally {
2557                 Binder.restoreCallingIdentity(ident);
2558             }
2559         }
2560 
2561         // IJobScheduler implementation
2562         @Override
enqueue(JobInfo job, JobWorkItem work)2563         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2564             if (DEBUG) {
2565                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2566             }
2567             final int uid = Binder.getCallingUid();
2568             final int userId = UserHandle.getUserId(uid);
2569 
2570             enforceValidJobRequest(uid, job);
2571             if (job.isPersisted()) {
2572                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2573             }
2574             if (work == null) {
2575                 throw new NullPointerException("work is null");
2576             }
2577 
2578             validateJobFlags(job, uid);
2579 
2580             long ident = Binder.clearCallingIdentity();
2581             try {
2582                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2583                         null);
2584             } finally {
2585                 Binder.restoreCallingIdentity(ident);
2586             }
2587         }
2588 
2589         @Override
scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)2590         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
2591                 throws RemoteException {
2592             final int callerUid = Binder.getCallingUid();
2593             if (DEBUG) {
2594                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
2595                         + " on behalf of " + packageName + "/");
2596             }
2597 
2598             if (packageName == null) {
2599                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
2600             }
2601 
2602             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2603                     android.Manifest.permission.UPDATE_DEVICE_STATS);
2604             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2605                 throw new SecurityException("Caller uid " + callerUid
2606                         + " not permitted to schedule jobs for other apps");
2607             }
2608 
2609             validateJobFlags(job, callerUid);
2610 
2611             long ident = Binder.clearCallingIdentity();
2612             try {
2613                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
2614                         packageName, userId, tag);
2615             } finally {
2616                 Binder.restoreCallingIdentity(ident);
2617             }
2618         }
2619 
2620         @Override
getAllPendingJobs()2621         public List<JobInfo> getAllPendingJobs() throws RemoteException {
2622             final int uid = Binder.getCallingUid();
2623 
2624             long ident = Binder.clearCallingIdentity();
2625             try {
2626                 return JobSchedulerService.this.getPendingJobs(uid);
2627             } finally {
2628                 Binder.restoreCallingIdentity(ident);
2629             }
2630         }
2631 
2632         @Override
getPendingJob(int jobId)2633         public JobInfo getPendingJob(int jobId) throws RemoteException {
2634             final int uid = Binder.getCallingUid();
2635 
2636             long ident = Binder.clearCallingIdentity();
2637             try {
2638                 return JobSchedulerService.this.getPendingJob(uid, jobId);
2639             } finally {
2640                 Binder.restoreCallingIdentity(ident);
2641             }
2642         }
2643 
2644         @Override
cancelAll()2645         public void cancelAll() throws RemoteException {
2646             final int uid = Binder.getCallingUid();
2647             long ident = Binder.clearCallingIdentity();
2648             try {
2649                 JobSchedulerService.this.cancelJobsForUid(uid,
2650                         "cancelAll() called by app, callingUid=" + uid);
2651             } finally {
2652                 Binder.restoreCallingIdentity(ident);
2653             }
2654         }
2655 
2656         @Override
cancel(int jobId)2657         public void cancel(int jobId) throws RemoteException {
2658             final int uid = Binder.getCallingUid();
2659 
2660             long ident = Binder.clearCallingIdentity();
2661             try {
2662                 JobSchedulerService.this.cancelJob(uid, jobId, uid);
2663             } finally {
2664                 Binder.restoreCallingIdentity(ident);
2665             }
2666         }
2667 
2668         /**
2669          * "dumpsys" infrastructure
2670          */
2671         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)2672         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2673             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
2674 
2675             int filterUid = -1;
2676             boolean proto = false;
2677             if (!ArrayUtils.isEmpty(args)) {
2678                 int opti = 0;
2679                 while (opti < args.length) {
2680                     String arg = args[opti];
2681                     if ("-h".equals(arg)) {
2682                         dumpHelp(pw);
2683                         return;
2684                     } else if ("-a".equals(arg)) {
2685                         // Ignore, we always dump all.
2686                     } else if ("--proto".equals(arg)) {
2687                         proto = true;
2688                     } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2689                         pw.println("Unknown option: " + arg);
2690                         return;
2691                     } else {
2692                         break;
2693                     }
2694                     opti++;
2695                 }
2696                 if (opti < args.length) {
2697                     String pkg = args[opti];
2698                     try {
2699                         filterUid = getContext().getPackageManager().getPackageUid(pkg,
2700                                 PackageManager.MATCH_ANY_USER);
2701                     } catch (NameNotFoundException ignored) {
2702                         pw.println("Invalid package: " + pkg);
2703                         return;
2704                     }
2705                 }
2706             }
2707 
2708             final long identityToken = Binder.clearCallingIdentity();
2709             try {
2710                 if (proto) {
2711                     JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2712                 } else {
2713                     JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, "  "),
2714                             filterUid);
2715                 }
2716             } finally {
2717                 Binder.restoreCallingIdentity(identityToken);
2718             }
2719         }
2720 
2721         @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)2722         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2723                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
2724                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2725                         this, in, out, err, args, callback, resultReceiver);
2726         }
2727     };
2728 
2729     // Shell command infrastructure: run the given job immediately
executeRunCommand(String pkgName, int userId, int jobId, boolean force)2730     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2731         if (DEBUG) {
2732             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2733                     + " " + jobId + " f=" + force);
2734         }
2735 
2736         try {
2737             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2738                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2739             if (uid < 0) {
2740                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2741             }
2742 
2743             synchronized (mLock) {
2744                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2745                 if (js == null) {
2746                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2747                 }
2748 
2749                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2750                 if (!js.isConstraintsSatisfied()) {
2751                     js.overrideState = 0;
2752                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2753                 }
2754 
2755                 queueReadyJobsForExecutionLocked();
2756                 maybeRunPendingJobsLocked();
2757             }
2758         } catch (RemoteException e) {
2759             // can't happen
2760         }
2761         return 0;
2762     }
2763 
2764     // Shell command infrastructure: immediately timeout currently executing jobs
executeTimeoutCommand(PrintWriter pw, String pkgName, int userId, boolean hasJobId, int jobId)2765     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2766             boolean hasJobId, int jobId) {
2767         if (DEBUG) {
2768             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2769         }
2770 
2771         synchronized (mLock) {
2772             boolean foundSome = false;
2773             for (int i=0; i<mActiveServices.size(); i++) {
2774                 final JobServiceContext jc = mActiveServices.get(i);
2775                 final JobStatus js = jc.getRunningJobLocked();
2776                 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
2777                     foundSome = true;
2778                     pw.print("Timing out: ");
2779                     js.printUniqueId(pw);
2780                     pw.print(" ");
2781                     pw.println(js.getServiceComponent().flattenToShortString());
2782                 }
2783             }
2784             if (!foundSome) {
2785                 pw.println("No matching executing jobs found.");
2786             }
2787         }
2788         return 0;
2789     }
2790 
2791     // Shell command infrastructure: cancel a scheduled job
executeCancelCommand(PrintWriter pw, String pkgName, int userId, boolean hasJobId, int jobId)2792     int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2793             boolean hasJobId, int jobId) {
2794         if (DEBUG) {
2795             Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2796         }
2797 
2798         int pkgUid = -1;
2799         try {
2800             IPackageManager pm = AppGlobals.getPackageManager();
2801             pkgUid = pm.getPackageUid(pkgName, 0, userId);
2802         } catch (RemoteException e) { /* can't happen */ }
2803 
2804         if (pkgUid < 0) {
2805             pw.println("Package " + pkgName + " not found.");
2806             return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2807         }
2808 
2809         if (!hasJobId) {
2810             pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2811             if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2812                 pw.println("No matching jobs found.");
2813             }
2814         } else {
2815             pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
2816             if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
2817                 pw.println("No matching job found.");
2818             }
2819         }
2820 
2821         return 0;
2822     }
2823 
setMonitorBattery(boolean enabled)2824     void setMonitorBattery(boolean enabled) {
2825         synchronized (mLock) {
2826             if (mBatteryController != null) {
2827                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2828             }
2829         }
2830     }
2831 
getBatterySeq()2832     int getBatterySeq() {
2833         synchronized (mLock) {
2834             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2835         }
2836     }
2837 
getBatteryCharging()2838     boolean getBatteryCharging() {
2839         synchronized (mLock) {
2840             return mBatteryController != null
2841                     ? mBatteryController.getTracker().isOnStablePower() : false;
2842         }
2843     }
2844 
getBatteryNotLow()2845     boolean getBatteryNotLow() {
2846         synchronized (mLock) {
2847             return mBatteryController != null
2848                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
2849         }
2850     }
2851 
getStorageSeq()2852     int getStorageSeq() {
2853         synchronized (mLock) {
2854             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2855         }
2856     }
2857 
getStorageNotLow()2858     boolean getStorageNotLow() {
2859         synchronized (mLock) {
2860             return mStorageController != null
2861                     ? mStorageController.getTracker().isStorageNotLow() : false;
2862         }
2863     }
2864 
getCurrentHeartbeat()2865     long getCurrentHeartbeat() {
2866         synchronized (mLock) {
2867             return mHeartbeat;
2868         }
2869     }
2870 
2871     // Shell command infrastructure
getJobState(PrintWriter pw, String pkgName, int userId, int jobId)2872     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2873         try {
2874             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2875                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2876             if (uid < 0) {
2877                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2878                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2879             }
2880 
2881             synchronized (mLock) {
2882                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2883                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2884                 if (js == null) {
2885                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
2886                     pw.print("/jid"); pw.print(jobId); pw.println(")");
2887                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2888                 }
2889 
2890                 boolean printed = false;
2891                 if (mPendingJobs.contains(js)) {
2892                     pw.print("pending");
2893                     printed = true;
2894                 }
2895                 if (isCurrentlyActiveLocked(js)) {
2896                     if (printed) {
2897                         pw.print(" ");
2898                     }
2899                     printed = true;
2900                     pw.println("active");
2901                 }
2902                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2903                     if (printed) {
2904                         pw.print(" ");
2905                     }
2906                     printed = true;
2907                     pw.println("user-stopped");
2908                 }
2909                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2910                     if (printed) {
2911                         pw.print(" ");
2912                     }
2913                     printed = true;
2914                     pw.println("backing-up");
2915                 }
2916                 boolean componentPresent = false;
2917                 try {
2918                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2919                             js.getServiceComponent(),
2920                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2921                             js.getUserId()) != null);
2922                 } catch (RemoteException e) {
2923                 }
2924                 if (!componentPresent) {
2925                     if (printed) {
2926                         pw.print(" ");
2927                     }
2928                     printed = true;
2929                     pw.println("no-component");
2930                 }
2931                 if (js.isReady()) {
2932                     if (printed) {
2933                         pw.print(" ");
2934                     }
2935                     printed = true;
2936                     pw.println("ready");
2937                 }
2938                 if (!printed) {
2939                     pw.print("waiting");
2940                 }
2941                 pw.println();
2942             }
2943         } catch (RemoteException e) {
2944             // can't happen
2945         }
2946         return 0;
2947     }
2948 
2949     // Shell command infrastructure
executeHeartbeatCommand(PrintWriter pw, int numBeats)2950     int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
2951         if (numBeats < 1) {
2952             pw.println(getCurrentHeartbeat());
2953             return 0;
2954         }
2955 
2956         pw.print("Advancing standby heartbeat by ");
2957         pw.println(numBeats);
2958         synchronized (mLock) {
2959             advanceHeartbeatLocked(numBeats);
2960         }
2961         return 0;
2962     }
2963 
triggerDockState(boolean idleState)2964     void triggerDockState(boolean idleState) {
2965         final Intent dockIntent;
2966         if (idleState) {
2967             dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
2968         } else {
2969             dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
2970         }
2971         dockIntent.setPackage("android");
2972         dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
2973         getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
2974     }
2975 
printContextIdToJobMap(JobStatus[] map, String initial)2976     private String printContextIdToJobMap(JobStatus[] map, String initial) {
2977         StringBuilder s = new StringBuilder(initial + ": ");
2978         for (int i=0; i<map.length; i++) {
2979             s.append("(")
2980                     .append(map[i] == null? -1: map[i].getJobId())
2981                     .append(map[i] == null? -1: map[i].getUid())
2982                     .append(")" );
2983         }
2984         return s.toString();
2985     }
2986 
printPendingQueue()2987     private String printPendingQueue() {
2988         StringBuilder s = new StringBuilder("Pending queue: ");
2989         Iterator<JobStatus> it = mPendingJobs.iterator();
2990         while (it.hasNext()) {
2991             JobStatus js = it.next();
2992             s.append("(")
2993                     .append(js.getJob().getId())
2994                     .append(", ")
2995                     .append(js.getUid())
2996                     .append(") ");
2997         }
2998         return s.toString();
2999     }
3000 
dumpHelp(PrintWriter pw)3001     static void dumpHelp(PrintWriter pw) {
3002         pw.println("Job Scheduler (jobscheduler) dump options:");
3003         pw.println("  [-h] [package] ...");
3004         pw.println("    -h: print this help");
3005         pw.println("  [package] is an optional package name to limit the output to.");
3006     }
3007 
3008     /** Sort jobs by caller UID, then by Job ID. */
sortJobs(List<JobStatus> jobs)3009     private static void sortJobs(List<JobStatus> jobs) {
3010         Collections.sort(jobs, new Comparator<JobStatus>() {
3011             @Override
3012             public int compare(JobStatus o1, JobStatus o2) {
3013                 int uid1 = o1.getUid();
3014                 int uid2 = o2.getUid();
3015                 int id1 = o1.getJobId();
3016                 int id2 = o2.getJobId();
3017                 if (uid1 != uid2) {
3018                     return uid1 < uid2 ? -1 : 1;
3019                 }
3020                 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
3021             }
3022         });
3023     }
3024 
dumpInternal(final IndentingPrintWriter pw, int filterUid)3025     void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
3026         final int filterUidFinal = UserHandle.getAppId(filterUid);
3027         final long nowElapsed = sElapsedRealtimeClock.millis();
3028         final long nowUptime = sUptimeMillisClock.millis();
3029         final Predicate<JobStatus> predicate = (js) -> {
3030             return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3031                     || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3032         };
3033         synchronized (mLock) {
3034             mConstants.dump(pw);
3035             pw.println();
3036 
3037             pw.println("  Heartbeat:");
3038             pw.print("    Current:    "); pw.println(mHeartbeat);
3039             pw.println("    Next");
3040             pw.print("      ACTIVE:   "); pw.println(mNextBucketHeartbeat[0]);
3041             pw.print("      WORKING:  "); pw.println(mNextBucketHeartbeat[1]);
3042             pw.print("      FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3043             pw.print("      RARE:     "); pw.println(mNextBucketHeartbeat[3]);
3044             pw.print("    Last heartbeat: ");
3045             TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3046             pw.println();
3047             pw.print("    Next heartbeat: ");
3048             TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3049                     nowElapsed, pw);
3050             pw.println();
3051             pw.print("    In parole?: ");
3052             pw.print(mInParole);
3053             pw.println();
3054             pw.println();
3055 
3056             pw.println("Started users: " + Arrays.toString(mStartedUsers));
3057             pw.print("Registered ");
3058             pw.print(mJobs.size());
3059             pw.println(" jobs:");
3060             if (mJobs.size() > 0) {
3061                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3062                 sortJobs(jobs);
3063                 for (JobStatus job : jobs) {
3064                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
3065                     pw.println(job.toShortStringExceptUniqueId());
3066 
3067                     // Skip printing details if the caller requested a filter
3068                     if (!predicate.test(job)) {
3069                         continue;
3070                     }
3071 
3072                     job.dump(pw, "    ", true, nowElapsed);
3073                     pw.print("    Last run heartbeat: ");
3074                     pw.print(heartbeatWhenJobsLastRun(job));
3075                     pw.println();
3076 
3077                     pw.print("    Ready: ");
3078                     pw.print(isReadyToBeExecutedLocked(job));
3079                     pw.print(" (job=");
3080                     pw.print(job.isReady());
3081                     pw.print(" user=");
3082                     pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
3083                     pw.print(" !pending=");
3084                     pw.print(!mPendingJobs.contains(job));
3085                     pw.print(" !active=");
3086                     pw.print(!isCurrentlyActiveLocked(job));
3087                     pw.print(" !backingup=");
3088                     pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
3089                     pw.print(" comp=");
3090                     boolean componentPresent = false;
3091                     try {
3092                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3093                                 job.getServiceComponent(),
3094                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3095                                 job.getUserId()) != null);
3096                     } catch (RemoteException e) {
3097                     }
3098                     pw.print(componentPresent);
3099                     pw.println(")");
3100                 }
3101             } else {
3102                 pw.println("  None.");
3103             }
3104             for (int i=0; i<mControllers.size(); i++) {
3105                 pw.println();
3106                 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3107                 pw.increaseIndent();
3108                 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3109                 pw.decreaseIndent();
3110             }
3111             pw.println();
3112             pw.println("Uid priority overrides:");
3113             for (int i=0; i< mUidPriorityOverride.size(); i++) {
3114                 int uid = mUidPriorityOverride.keyAt(i);
3115                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3116                     pw.print("  "); pw.print(UserHandle.formatUid(uid));
3117                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3118                 }
3119             }
3120             if (mBackingUpUids.size() > 0) {
3121                 pw.println();
3122                 pw.println("Backing up uids:");
3123                 boolean first = true;
3124                 for (int i = 0; i < mBackingUpUids.size(); i++) {
3125                     int uid = mBackingUpUids.keyAt(i);
3126                     if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3127                         if (first) {
3128                             pw.print("  ");
3129                             first = false;
3130                         } else {
3131                             pw.print(", ");
3132                         }
3133                         pw.print(UserHandle.formatUid(uid));
3134                     }
3135                 }
3136                 pw.println();
3137             }
3138             pw.println();
3139             mJobPackageTracker.dump(pw, "", filterUidFinal);
3140             pw.println();
3141             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3142                 pw.println();
3143             }
3144             pw.println("Pending queue:");
3145             for (int i=0; i<mPendingJobs.size(); i++) {
3146                 JobStatus job = mPendingJobs.get(i);
3147                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
3148                 pw.println(job.toShortString());
3149                 job.dump(pw, "    ", false, nowElapsed);
3150                 int priority = evaluateJobPriorityLocked(job);
3151                 if (priority != JobInfo.PRIORITY_DEFAULT) {
3152                     pw.print("    Evaluated priority: "); pw.println(priority);
3153                 }
3154                 pw.print("    Tag: "); pw.println(job.getTag());
3155                 pw.print("    Enq: ");
3156                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
3157                 pw.println();
3158             }
3159             pw.println();
3160             pw.println("Active jobs:");
3161             for (int i=0; i<mActiveServices.size(); i++) {
3162                 JobServiceContext jsc = mActiveServices.get(i);
3163                 pw.print("  Slot #"); pw.print(i); pw.print(": ");
3164                 final JobStatus job = jsc.getRunningJobLocked();
3165                 if (job == null) {
3166                     if (jsc.mStoppedReason != null) {
3167                         pw.print("inactive since ");
3168                         TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3169                         pw.print(", stopped because: ");
3170                         pw.println(jsc.mStoppedReason);
3171                     } else {
3172                         pw.println("inactive");
3173                     }
3174                     continue;
3175                 } else {
3176                     pw.println(job.toShortString());
3177                     pw.print("    Running for: ");
3178                     TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
3179                     pw.print(", timeout at: ");
3180                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
3181                     pw.println();
3182                     job.dump(pw, "    ", false, nowElapsed);
3183                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3184                     if (priority != JobInfo.PRIORITY_DEFAULT) {
3185                         pw.print("    Evaluated priority: "); pw.println(priority);
3186                     }
3187                     pw.print("    Active at ");
3188                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
3189                     pw.print(", pending for ");
3190                     TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3191                     pw.println();
3192                 }
3193             }
3194             if (filterUid == -1) {
3195                 pw.println();
3196                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3197                 pw.print("mReportedActive="); pw.println(mReportedActive);
3198                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3199             }
3200             pw.println();
3201             pw.print("PersistStats: ");
3202             pw.println(mJobs.getPersistStats());
3203         }
3204         pw.println();
3205     }
3206 
dumpInternalProto(final FileDescriptor fd, int filterUid)3207     void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3208         ProtoOutputStream proto = new ProtoOutputStream(fd);
3209         final int filterUidFinal = UserHandle.getAppId(filterUid);
3210         final long nowElapsed = sElapsedRealtimeClock.millis();
3211         final long nowUptime = sUptimeMillisClock.millis();
3212         final Predicate<JobStatus> predicate = (js) -> {
3213             return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3214                     || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3215         };
3216 
3217         synchronized (mLock) {
3218             mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
3219             proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3220             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3221             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3222             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3223             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3224             proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3225                     mLastHeartbeatTime - nowUptime);
3226             proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3227                     mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
3228             proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
3229 
3230             for (int u : mStartedUsers) {
3231                 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3232             }
3233             if (mJobs.size() > 0) {
3234                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3235                 sortJobs(jobs);
3236                 for (JobStatus job : jobs) {
3237                     final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3238                     job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3239 
3240                     // Skip printing details if the caller requested a filter
3241                     if (!predicate.test(job)) {
3242                         continue;
3243                     }
3244 
3245                     job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3246 
3247                     // isReadyToBeExecuted
3248                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3249                             job.isReady());
3250                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3251                             ArrayUtils.contains(mStartedUsers, job.getUserId()));
3252                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3253                             mPendingJobs.contains(job));
3254                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3255                             isCurrentlyActiveLocked(job));
3256                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3257                             mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3258                     boolean componentPresent = false;
3259                     try {
3260                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3261                                 job.getServiceComponent(),
3262                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3263                                 job.getUserId()) != null);
3264                     } catch (RemoteException e) {
3265                     }
3266                     proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3267                             componentPresent);
3268                     proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
3269 
3270                     proto.end(rjToken);
3271                 }
3272             }
3273             for (StateController controller : mControllers) {
3274                 controller.dumpControllerStateLocked(
3275                         proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
3276             }
3277             for (int i=0; i< mUidPriorityOverride.size(); i++) {
3278                 int uid = mUidPriorityOverride.keyAt(i);
3279                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3280                     long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3281                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3282                     proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3283                             mUidPriorityOverride.valueAt(i));
3284                     proto.end(pToken);
3285                 }
3286             }
3287             for (int i = 0; i < mBackingUpUids.size(); i++) {
3288                 int uid = mBackingUpUids.keyAt(i);
3289                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3290                     proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3291                 }
3292             }
3293 
3294             mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3295                     filterUidFinal);
3296             mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3297                     filterUidFinal);
3298 
3299             for (JobStatus job : mPendingJobs) {
3300                 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3301 
3302                 job.writeToShortProto(proto, PendingJob.INFO);
3303                 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3304                 int priority = evaluateJobPriorityLocked(job);
3305                 if (priority != JobInfo.PRIORITY_DEFAULT) {
3306                     proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3307                 }
3308                 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3309 
3310                 proto.end(pjToken);
3311             }
3312             for (JobServiceContext jsc : mActiveServices) {
3313                 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3314                 final JobStatus job = jsc.getRunningJobLocked();
3315 
3316                 if (job == null) {
3317                     final long ijToken = proto.start(ActiveJob.INACTIVE);
3318 
3319                         proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3320                                 nowElapsed - jsc.mStoppedTime);
3321                     if (jsc.mStoppedReason != null) {
3322                         proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3323                                 jsc.mStoppedReason);
3324                     }
3325 
3326                     proto.end(ijToken);
3327                 } else {
3328                     final long rjToken = proto.start(ActiveJob.RUNNING);
3329 
3330                     job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3331 
3332                     proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3333                             nowElapsed - jsc.getExecutionStartTimeElapsed());
3334                     proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3335                             jsc.getTimeoutElapsed() - nowElapsed);
3336 
3337                     job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3338 
3339                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3340                     if (priority != JobInfo.PRIORITY_DEFAULT) {
3341                         proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3342                     }
3343 
3344                     proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3345                             nowUptime - job.madeActive);
3346                     proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3347                             job.madeActive - job.madePending);
3348 
3349                     proto.end(rjToken);
3350                 }
3351                 proto.end(ajToken);
3352             }
3353             if (filterUid == -1) {
3354                 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3355                 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3356                 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3357             }
3358         }
3359 
3360         proto.flush();
3361     }
3362 }
3363