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 java.io.FileDescriptor;
23 import java.io.PrintWriter;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Comparator;
28 import java.util.Iterator;
29 import java.util.List;
30 
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.app.AppGlobals;
34 import android.app.IUidObserver;
35 import android.app.job.JobInfo;
36 import android.app.job.JobParameters;
37 import android.app.job.JobScheduler;
38 import android.app.job.JobService;
39 import android.app.job.IJobScheduler;
40 import android.app.job.JobWorkItem;
41 import android.content.BroadcastReceiver;
42 import android.content.ComponentName;
43 import android.content.ContentResolver;
44 import android.content.Context;
45 import android.content.Intent;
46 import android.content.IntentFilter;
47 import android.content.pm.IPackageManager;
48 import android.content.pm.PackageManager;
49 import android.content.pm.ServiceInfo;
50 import android.content.pm.PackageManager.NameNotFoundException;
51 import android.database.ContentObserver;
52 import android.net.Uri;
53 import android.os.BatteryStats;
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.PowerManager;
60 import android.os.RemoteException;
61 import android.os.ResultReceiver;
62 import android.os.ServiceManager;
63 import android.os.ShellCallback;
64 import android.os.SystemClock;
65 import android.os.UserHandle;
66 import android.os.UserManagerInternal;
67 import android.provider.Settings;
68 import android.util.KeyValueListParser;
69 import android.util.Slog;
70 import android.util.SparseArray;
71 import android.util.SparseIntArray;
72 import android.util.TimeUtils;
73 
74 import com.android.internal.app.IBatteryStats;
75 import com.android.internal.app.procstats.ProcessStats;
76 import com.android.internal.util.ArrayUtils;
77 import com.android.internal.util.DumpUtils;
78 import com.android.server.DeviceIdleController;
79 import com.android.server.LocalServices;
80 import com.android.server.job.JobStore.JobStatusFunctor;
81 import com.android.server.job.controllers.AppIdleController;
82 import com.android.server.job.controllers.BatteryController;
83 import com.android.server.job.controllers.ConnectivityController;
84 import com.android.server.job.controllers.ContentObserverController;
85 import com.android.server.job.controllers.DeviceIdleJobsController;
86 import com.android.server.job.controllers.IdleController;
87 import com.android.server.job.controllers.JobStatus;
88 import com.android.server.job.controllers.StateController;
89 import com.android.server.job.controllers.StorageController;
90 import com.android.server.job.controllers.TimeController;
91 
92 import libcore.util.EmptyArray;
93 
94 /**
95  * Responsible for taking jobs representing work to be performed by a client app, and determining
96  * based on the criteria specified when that job should be run against the client application's
97  * endpoint.
98  * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
99  * about constraints, or the state of active jobs. It receives callbacks from the various
100  * controllers and completed jobs and operates accordingly.
101  *
102  * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
103  * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
104  * @hide
105  */
106 public final class JobSchedulerService extends com.android.server.SystemService
107         implements StateChangedListener, JobCompletedListener {
108     static final String TAG = "JobSchedulerService";
109     public static final boolean DEBUG = false;
110 
111     /** The maximum number of concurrent jobs we run at one time. */
112     private static final int MAX_JOB_CONTEXTS_COUNT = 16;
113     /** Enforce a per-app limit on scheduled jobs? */
114     private static final boolean ENFORCE_MAX_JOBS = true;
115     /** The maximum number of jobs that we allow an unprivileged app to schedule */
116     private static final int MAX_JOBS_PER_APP = 100;
117 
118 
119     /** Global local for all job scheduler state. */
120     final Object mLock = new Object();
121     /** Master list of jobs. */
122     final JobStore mJobs;
123     /** Tracking amount of time each package runs for. */
124     final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
125 
126     static final int MSG_JOB_EXPIRED = 0;
127     static final int MSG_CHECK_JOB = 1;
128     static final int MSG_STOP_JOB = 2;
129     static final int MSG_CHECK_JOB_GREEDY = 3;
130 
131     /**
132      * Track Services that have currently active or pending jobs. The index is provided by
133      * {@link JobStatus#getServiceToken()}
134      */
135     final List<JobServiceContext> mActiveServices = new ArrayList<>();
136     /** List of controllers that will notify this service of updates to jobs. */
137     List<StateController> mControllers;
138     /** Need direct access to this for testing. */
139     BatteryController mBatteryController;
140     /** Need direct access to this for testing. */
141     StorageController mStorageController;
142     /**
143      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
144      * when ready to execute them.
145      */
146     final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
147 
148     int[] mStartedUsers = EmptyArray.INT;
149 
150     final JobHandler mHandler;
151     final JobSchedulerStub mJobSchedulerStub;
152 
153     IBatteryStats mBatteryStats;
154     PowerManager mPowerManager;
155     DeviceIdleController.LocalService mLocalDeviceIdleController;
156 
157     /**
158      * Set to true once we are allowed to run third party apps.
159      */
160     boolean mReadyToRock;
161 
162     /**
163      * What we last reported to DeviceIdleController about whether we are active.
164      */
165     boolean mReportedActive;
166 
167     /**
168      * Current limit on the number of concurrent JobServiceContext entries we want to
169      * keep actively running a job.
170      */
171     int mMaxActiveJobs = 1;
172 
173     /**
174      * Which uids are currently in the foreground.
175      */
176     final SparseIntArray mUidPriorityOverride = new SparseIntArray();
177 
178     /**
179      * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
180      */
181     final SparseIntArray mBackingUpUids = new SparseIntArray();
182 
183     // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
184 
185     /**
186      * This array essentially stores the state of mActiveServices array.
187      * The ith index stores the job present on the ith JobServiceContext.
188      * We manipulate this array until we arrive at what jobs should be running on
189      * what JobServiceContext.
190      */
191     JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
192     /**
193      * Indicates whether we need to act on this jobContext id
194      */
195     boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
196     /**
197      * The uid whose jobs we would like to assign to a context.
198      */
199     int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
200 
201     /**
202      * All times are in milliseconds. These constants are kept synchronized with the system
203      * global Settings. Any access to this class or its fields should be done while
204      * holding the JobSchedulerService.mLock lock.
205      */
206     private final class Constants extends ContentObserver {
207         // Key names stored in the settings value.
208         private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
209         private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
210         private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
211         private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
212         private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
213         private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
214         private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
215         private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
216         private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
217         private static final String KEY_FG_JOB_COUNT = "fg_job_count";
218         private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
219         private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
220         private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
221         private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
222         private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
223                 = "max_standard_reschedule_count";
224         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
225         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
226         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
227 
228         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
229         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
230         private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
231         private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
232         private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
233         private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
234         private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
235         private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
236         private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
237         private static final int DEFAULT_FG_JOB_COUNT = 4;
238         private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
239         private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
240         private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
241         private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
242         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
243         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
244         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
245         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
246 
247         /**
248          * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
249          * early.
250          */
251         int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
252         /**
253          * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
254          * things early.
255          */
256         int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
257         /**
258          * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
259          * schedule things early.
260          */
261         int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
262         /**
263          * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
264          * schedule things early.
265          */
266         int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
267         /**
268          * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
269          * things early.  1 == Run connectivity jobs as soon as ready.
270          */
271         int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
272         /**
273          * Minimum # of content trigger jobs that must be ready in order to force the JMS to
274          * schedule things early.
275          */
276         int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
277         /**
278          * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
279          * running some work early.  This (and thus the other min counts) is now set to 1, to
280          * prevent any batching at this level.  Since we now do batching through doze, that is
281          * a much better mechanism.
282          */
283         int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
284         /**
285          * This is the job execution factor that is considered to be heavy use of the system.
286          */
287         float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
288         /**
289          * This is the job execution factor that is considered to be moderate use of the system.
290          */
291         float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
292         /**
293          * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
294          */
295         int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
296         /**
297          * The maximum number of background jobs we allow when the system is in a normal
298          * memory state.
299          */
300         int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
301         /**
302          * The maximum number of background jobs we allow when the system is in a moderate
303          * memory state.
304          */
305         int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
306         /**
307          * The maximum number of background jobs we allow when the system is in a low
308          * memory state.
309          */
310         int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
311         /**
312          * The maximum number of background jobs we allow when the system is in a critical
313          * memory state.
314          */
315         int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
316         /**
317          * The maximum number of times we allow a job to have itself rescheduled before
318          * giving up on it, for standard jobs.
319          */
320         int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
321         /**
322          * The maximum number of times we allow a job to have itself rescheduled before
323          * giving up on it, for jobs that are executing work.
324          */
325         int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
326         /**
327          * The minimum backoff time to allow for linear backoff.
328          */
329         long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
330         /**
331          * The minimum backoff time to allow for exponential backoff.
332          */
333         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
334 
335         private ContentResolver mResolver;
336         private final KeyValueListParser mParser = new KeyValueListParser(',');
337 
Constants(Handler handler)338         public Constants(Handler handler) {
339             super(handler);
340         }
341 
start(ContentResolver resolver)342         public void start(ContentResolver resolver) {
343             mResolver = resolver;
344             mResolver.registerContentObserver(Settings.Global.getUriFor(
345                     Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
346             updateConstants();
347         }
348 
349         @Override
onChange(boolean selfChange, Uri uri)350         public void onChange(boolean selfChange, Uri uri) {
351             updateConstants();
352         }
353 
updateConstants()354         private void updateConstants() {
355             synchronized (mLock) {
356                 try {
357                     mParser.setString(Settings.Global.getString(mResolver,
358                             Settings.Global.ALARM_MANAGER_CONSTANTS));
359                 } catch (IllegalArgumentException e) {
360                     // Failed to parse the settings string, log this and move on
361                     // with defaults.
362                     Slog.e(TAG, "Bad device idle settings", e);
363                 }
364 
365                 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
366                         DEFAULT_MIN_IDLE_COUNT);
367                 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
368                         DEFAULT_MIN_CHARGING_COUNT);
369                 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
370                         DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
371                 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
372                         DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
373                 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
374                         DEFAULT_MIN_CONNECTIVITY_COUNT);
375                 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
376                         DEFAULT_MIN_CONTENT_COUNT);
377                 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
378                         DEFAULT_MIN_READY_JOBS_COUNT);
379                 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
380                         DEFAULT_HEAVY_USE_FACTOR);
381                 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
382                         DEFAULT_MODERATE_USE_FACTOR);
383                 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
384                         DEFAULT_FG_JOB_COUNT);
385                 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
386                         DEFAULT_BG_NORMAL_JOB_COUNT);
387                 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
388                     BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
389                 }
390                 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
391                         DEFAULT_BG_MODERATE_JOB_COUNT);
392                 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
393                     BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
394                 }
395                 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
396                         DEFAULT_BG_LOW_JOB_COUNT);
397                 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
398                     BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
399                 }
400                 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
401                         DEFAULT_BG_CRITICAL_JOB_COUNT);
402                 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
403                     BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
404                 }
405                 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
406                         DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
407                 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
408                         DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
409                 MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
410                         DEFAULT_MIN_LINEAR_BACKOFF_TIME);
411                 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
412                         DEFAULT_MIN_EXP_BACKOFF_TIME);
413             }
414         }
415 
dump(PrintWriter pw)416         void dump(PrintWriter pw) {
417             pw.println("  Settings:");
418 
419             pw.print("    "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
420             pw.print(MIN_IDLE_COUNT); pw.println();
421 
422             pw.print("    "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
423             pw.print(MIN_CHARGING_COUNT); pw.println();
424 
425             pw.print("    "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
426             pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
427 
428             pw.print("    "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
429             pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
430 
431             pw.print("    "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
432             pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
433 
434             pw.print("    "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
435             pw.print(MIN_CONTENT_COUNT); pw.println();
436 
437             pw.print("    "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
438             pw.print(MIN_READY_JOBS_COUNT); pw.println();
439 
440             pw.print("    "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
441             pw.print(HEAVY_USE_FACTOR); pw.println();
442 
443             pw.print("    "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
444             pw.print(MODERATE_USE_FACTOR); pw.println();
445 
446             pw.print("    "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
447             pw.print(FG_JOB_COUNT); pw.println();
448 
449             pw.print("    "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
450             pw.print(BG_NORMAL_JOB_COUNT); pw.println();
451 
452             pw.print("    "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
453             pw.print(BG_MODERATE_JOB_COUNT); pw.println();
454 
455             pw.print("    "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
456             pw.print(BG_LOW_JOB_COUNT); pw.println();
457 
458             pw.print("    "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
459             pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
460 
461             pw.print("    "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
462             pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
463 
464             pw.print("    "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
465             pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
466 
467             pw.print("    "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
468             pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
469 
470             pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
471             pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
472         }
473     }
474 
475     final Constants mConstants;
476 
477     static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
478         if (o1.enqueueTime < o2.enqueueTime) {
479             return -1;
480         }
481         return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
482     };
483 
addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator)484     static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
485         int where = Collections.binarySearch(array, newItem, comparator);
486         if (where < 0) {
487             where = ~where;
488         }
489         array.add(where, newItem);
490     }
491 
492     /**
493      * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
494      * still clean up. On reinstall the package will have a new uid.
495      */
496     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
497         @Override
498         public void onReceive(Context context, Intent intent) {
499             final String action = intent.getAction();
500             if (DEBUG) {
501                 Slog.d(TAG, "Receieved: " + action);
502             }
503             if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
504                 // Purge the app's jobs if the whole package was just disabled.  When this is
505                 // the case the component name will be a bare package name.
506                 final String pkgName = getPackageName(intent);
507                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
508                 if (pkgName != null && pkgUid != -1) {
509                     final String[] changedComponents = intent.getStringArrayExtra(
510                             Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
511                     if (changedComponents != null) {
512                         for (String component : changedComponents) {
513                             if (component.equals(pkgName)) {
514                                 if (DEBUG) {
515                                     Slog.d(TAG, "Package state change: " + pkgName);
516                                 }
517                                 try {
518                                     final int userId = UserHandle.getUserId(pkgUid);
519                                     IPackageManager pm = AppGlobals.getPackageManager();
520                                     final int state = pm.getApplicationEnabledSetting(pkgName, userId);
521                                     if (state == COMPONENT_ENABLED_STATE_DISABLED
522                                             || state ==  COMPONENT_ENABLED_STATE_DISABLED_USER) {
523                                         if (DEBUG) {
524                                             Slog.d(TAG, "Removing jobs for package " + pkgName
525                                                     + " in user " + userId);
526                                         }
527                                         cancelJobsForUid(pkgUid, "app package state changed");
528                                     }
529                                 } catch (RemoteException|IllegalArgumentException e) {
530                                     /*
531                                      * IllegalArgumentException means that the package doesn't exist.
532                                      * This arises when PACKAGE_CHANGED broadcast delivery has lagged
533                                      * behind outright uninstall, so by the time we try to act it's gone.
534                                      * We don't need to act on this PACKAGE_CHANGED when this happens;
535                                      * we'll get a PACKAGE_REMOVED later and clean up then.
536                                      *
537                                      * RemoteException can't actually happen; the package manager is
538                                      * running in this same process.
539                                      */
540                                 }
541                                 break;
542                             }
543                         }
544                     }
545                 } else {
546                     Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
547                 }
548             } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
549                 // If this is an outright uninstall rather than the first half of an
550                 // app update sequence, cancel the jobs associated with the app.
551                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
552                     int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
553                     if (DEBUG) {
554                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
555                     }
556                     cancelJobsForUid(uidRemoved, "app uninstalled");
557                 }
558             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
559                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
560                 if (DEBUG) {
561                     Slog.d(TAG, "Removing jobs for user: " + userId);
562                 }
563                 cancelJobsForUser(userId);
564             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
565                 // Has this package scheduled any jobs, such that we will take action
566                 // if it were to be force-stopped?
567                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
568                 final String pkgName = intent.getData().getSchemeSpecificPart();
569                 if (pkgUid != -1) {
570                     List<JobStatus> jobsForUid;
571                     synchronized (mLock) {
572                         jobsForUid = mJobs.getJobsByUid(pkgUid);
573                     }
574                     for (int i = jobsForUid.size() - 1; i >= 0; i--) {
575                         if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
576                             if (DEBUG) {
577                                 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
578                                         + pkgUid + " has jobs");
579                             }
580                             setResultCode(Activity.RESULT_OK);
581                             break;
582                         }
583                     }
584                 }
585             } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
586                 // possible force-stop
587                 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
588                 final String pkgName = intent.getData().getSchemeSpecificPart();
589                 if (pkgUid != -1) {
590                     if (DEBUG) {
591                         Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
592                     }
593                     cancelJobsForPackageAndUid(pkgName, pkgUid);
594                 }
595             }
596         }
597     };
598 
getPackageName(Intent intent)599     private String getPackageName(Intent intent) {
600         Uri uri = intent.getData();
601         String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
602         return pkg;
603     }
604 
605     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
606         @Override public void onUidStateChanged(int uid, int procState,
607                 long procStateSeq) throws RemoteException {
608             updateUidState(uid, procState);
609         }
610 
611         @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
612             updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
613             if (disabled) {
614                 cancelJobsForUid(uid, "uid gone");
615             }
616         }
617 
618         @Override public void onUidActive(int uid) throws RemoteException {
619         }
620 
621         @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
622             if (disabled) {
623                 cancelJobsForUid(uid, "app uid idle");
624             }
625         }
626     };
627 
getLock()628     public Object getLock() {
629         return mLock;
630     }
631 
getJobStore()632     public JobStore getJobStore() {
633         return mJobs;
634     }
635 
636     @Override
onStartUser(int userHandle)637     public void onStartUser(int userHandle) {
638         mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
639         // Let's kick any outstanding jobs for this user.
640         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
641     }
642 
643     @Override
onUnlockUser(int userHandle)644     public void onUnlockUser(int userHandle) {
645         // Let's kick any outstanding jobs for this user.
646         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
647     }
648 
649     @Override
onStopUser(int userHandle)650     public void onStopUser(int userHandle) {
651         mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
652     }
653 
scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName, int userId, String tag)654     public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
655             int userId, String tag) {
656         try {
657             if (ActivityManager.getService().isAppStartModeDisabled(uId,
658                     job.getService().getPackageName())) {
659                 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
660                         + " -- package not allowed to start");
661                 return JobScheduler.RESULT_FAILURE;
662             }
663         } catch (RemoteException e) {
664         }
665         synchronized (mLock) {
666             final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
667 
668             if (work != null && toCancel != null) {
669                 // Fast path: we are adding work to an existing job, and the JobInfo is not
670                 // changing.  We can just directly enqueue this work in to the job.
671                 if (toCancel.getJob().equals(job)) {
672                     toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
673                     return JobScheduler.RESULT_SUCCESS;
674                 }
675             }
676 
677             JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
678             if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
679             // Jobs on behalf of others don't apply to the per-app job cap
680             if (ENFORCE_MAX_JOBS && packageName == null) {
681                 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
682                     Slog.w(TAG, "Too many jobs for uid " + uId);
683                     throw new IllegalStateException("Apps may not schedule more than "
684                                 + MAX_JOBS_PER_APP + " distinct jobs");
685                 }
686             }
687 
688             // This may throw a SecurityException.
689             jobStatus.prepareLocked(ActivityManager.getService());
690 
691             if (toCancel != null) {
692                 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
693             }
694             if (work != null) {
695                 // If work has been supplied, enqueue it into the new job.
696                 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
697             }
698             startTrackingJobLocked(jobStatus, toCancel);
699 
700             // If the job is immediately ready to run, then we can just immediately
701             // put it in the pending list and try to schedule it.  This is especially
702             // important for jobs with a 0 deadline constraint, since they will happen a fair
703             // amount, we want to handle them as quickly as possible, and semantically we want to
704             // make sure we have started holding the wake lock for the job before returning to
705             // the caller.
706             // If the job is not yet ready to run, there is nothing more to do -- we are
707             // now just waiting for one of its controllers to change state and schedule
708             // the job appropriately.
709             if (isReadyToBeExecutedLocked(jobStatus)) {
710                 // This is a new job, we can just immediately put it on the pending
711                 // list and try to run it.
712                 mJobPackageTracker.notePending(jobStatus);
713                 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
714                 maybeRunPendingJobsLocked();
715             }
716         }
717         return JobScheduler.RESULT_SUCCESS;
718     }
719 
getPendingJobs(int uid)720     public List<JobInfo> getPendingJobs(int uid) {
721         synchronized (mLock) {
722             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
723             ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
724             for (int i = jobs.size() - 1; i >= 0; i--) {
725                 JobStatus job = jobs.get(i);
726                 outList.add(job.getJob());
727             }
728             return outList;
729         }
730     }
731 
getPendingJob(int uid, int jobId)732     public JobInfo getPendingJob(int uid, int jobId) {
733         synchronized (mLock) {
734             List<JobStatus> jobs = mJobs.getJobsByUid(uid);
735             for (int i = jobs.size() - 1; i >= 0; i--) {
736                 JobStatus job = jobs.get(i);
737                 if (job.getJobId() == jobId) {
738                     return job.getJob();
739                 }
740             }
741             return null;
742         }
743     }
744 
cancelJobsForUser(int userHandle)745     void cancelJobsForUser(int userHandle) {
746         synchronized (mLock) {
747             final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
748             for (int i=0; i<jobsForUser.size(); i++) {
749                 JobStatus toRemove = jobsForUser.get(i);
750                 cancelJobImplLocked(toRemove, null, "user removed");
751             }
752         }
753     }
754 
cancelJobsForNonExistentUsers()755     private void cancelJobsForNonExistentUsers() {
756         UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
757         synchronized (mLock) {
758             mJobs.removeJobsOfNonUsers(umi.getUserIds());
759         }
760     }
761 
cancelJobsForPackageAndUid(String pkgName, int uid)762     void cancelJobsForPackageAndUid(String pkgName, int uid) {
763         synchronized (mLock) {
764             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
765             for (int i = jobsForUid.size() - 1; i >= 0; i--) {
766                 final JobStatus job = jobsForUid.get(i);
767                 if (job.getSourcePackageName().equals(pkgName)) {
768                     cancelJobImplLocked(job, null, "app force stopped");
769                 }
770             }
771         }
772     }
773 
774     /**
775      * Entry point from client to cancel all jobs originating from their uid.
776      * This will remove the job from the master list, and cancel the job if it was staged for
777      * execution or being executed.
778      * @param uid Uid to check against for removal of a job.
779      *
780      */
cancelJobsForUid(int uid, String reason)781     public void cancelJobsForUid(int uid, String reason) {
782         synchronized (mLock) {
783             final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
784             for (int i=0; i<jobsForUid.size(); i++) {
785                 JobStatus toRemove = jobsForUid.get(i);
786                 cancelJobImplLocked(toRemove, null, reason);
787             }
788         }
789     }
790 
791     /**
792      * Entry point from client to cancel the job corresponding to the jobId provided.
793      * This will remove the job from the master list, and cancel the job if it was staged for
794      * execution or being executed.
795      * @param uid Uid of the calling client.
796      * @param jobId Id of the job, provided at schedule-time.
797      */
cancelJob(int uid, int jobId)798     public void cancelJob(int uid, int jobId) {
799         JobStatus toCancel;
800         synchronized (mLock) {
801             toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
802             if (toCancel != null) {
803                 cancelJobImplLocked(toCancel, null, "cancel() called by app");
804             }
805         }
806     }
807 
cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason)808     private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
809         if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
810         cancelled.unprepareLocked(ActivityManager.getService());
811         stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
812         // Remove from pending queue.
813         if (mPendingJobs.remove(cancelled)) {
814             mJobPackageTracker.noteNonpending(cancelled);
815         }
816         // Cancel if running.
817         stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
818         reportActiveLocked();
819     }
820 
updateUidState(int uid, int procState)821     void updateUidState(int uid, int procState) {
822         synchronized (mLock) {
823             if (procState == ActivityManager.PROCESS_STATE_TOP) {
824                 // Only use this if we are exactly the top app.  All others can live
825                 // with just the foreground priority.  This means that persistent processes
826                 // can never be the top app priority...  that is fine.
827                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
828             } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
829                 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
830             } else {
831                 mUidPriorityOverride.delete(uid);
832             }
833         }
834     }
835 
836     @Override
onDeviceIdleStateChanged(boolean deviceIdle)837     public void onDeviceIdleStateChanged(boolean deviceIdle) {
838         synchronized (mLock) {
839             if (deviceIdle) {
840                 // When becoming idle, make sure no jobs are actively running,
841                 // except those using the idle exemption flag.
842                 for (int i=0; i<mActiveServices.size(); i++) {
843                     JobServiceContext jsc = mActiveServices.get(i);
844                     final JobStatus executing = jsc.getRunningJobLocked();
845                     if (executing != null
846                             && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
847                         jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
848                                 "cancelled due to doze");
849                     }
850                 }
851             } else {
852                 // When coming out of idle, allow thing to start back up.
853                 if (mReadyToRock) {
854                     if (mLocalDeviceIdleController != null) {
855                         if (!mReportedActive) {
856                             mReportedActive = true;
857                             mLocalDeviceIdleController.setJobsActive(true);
858                         }
859                     }
860                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
861                 }
862             }
863         }
864     }
865 
reportActiveLocked()866     void reportActiveLocked() {
867         // active is true if pending queue contains jobs OR some job is running.
868         boolean active = mPendingJobs.size() > 0;
869         if (mPendingJobs.size() <= 0) {
870             for (int i=0; i<mActiveServices.size(); i++) {
871                 final JobServiceContext jsc = mActiveServices.get(i);
872                 final JobStatus job = jsc.getRunningJobLocked();
873                 if (job != null
874                         && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
875                         && !job.dozeWhitelisted) {
876                     // We will report active if we have a job running and it is not an exception
877                     // due to being in the foreground or whitelisted.
878                     active = true;
879                     break;
880                 }
881             }
882         }
883 
884         if (mReportedActive != active) {
885             mReportedActive = active;
886             if (mLocalDeviceIdleController != null) {
887                 mLocalDeviceIdleController.setJobsActive(active);
888             }
889         }
890     }
891 
892     /**
893      * Initializes the system service.
894      * <p>
895      * Subclasses must define a single argument constructor that accepts the context
896      * and passes it to super.
897      * </p>
898      *
899      * @param context The system server context.
900      */
JobSchedulerService(Context context)901     public JobSchedulerService(Context context) {
902         super(context);
903         mHandler = new JobHandler(context.getMainLooper());
904         mConstants = new Constants(mHandler);
905         mJobSchedulerStub = new JobSchedulerStub();
906         mJobs = JobStore.initAndGet(this);
907 
908         // Create the controllers.
909         mControllers = new ArrayList<StateController>();
910         mControllers.add(ConnectivityController.get(this));
911         mControllers.add(TimeController.get(this));
912         mControllers.add(IdleController.get(this));
913         mBatteryController = BatteryController.get(this);
914         mControllers.add(mBatteryController);
915         mStorageController = StorageController.get(this);
916         mControllers.add(mStorageController);
917         mControllers.add(AppIdleController.get(this));
918         mControllers.add(ContentObserverController.get(this));
919         mControllers.add(DeviceIdleJobsController.get(this));
920     }
921 
922     @Override
onStart()923     public void onStart() {
924         publishLocalService(JobSchedulerInternal.class, new LocalService());
925         publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
926     }
927 
928     @Override
onBootPhase(int phase)929     public void onBootPhase(int phase) {
930         if (PHASE_SYSTEM_SERVICES_READY == phase) {
931             mConstants.start(getContext().getContentResolver());
932             // Register br for package removals and user removals.
933             final IntentFilter filter = new IntentFilter();
934             filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
935             filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
936             filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
937             filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
938             filter.addDataScheme("package");
939             getContext().registerReceiverAsUser(
940                     mBroadcastReceiver, UserHandle.ALL, filter, null, null);
941             final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
942             getContext().registerReceiverAsUser(
943                     mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
944             mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
945             try {
946                 ActivityManager.getService().registerUidObserver(mUidObserver,
947                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
948                         | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
949                         null);
950             } catch (RemoteException e) {
951                 // ignored; both services live in system_server
952             }
953             // Remove any jobs that are not associated with any of the current users.
954             cancelJobsForNonExistentUsers();
955         } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
956             synchronized (mLock) {
957                 // Let's go!
958                 mReadyToRock = true;
959                 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
960                         BatteryStats.SERVICE_NAME));
961                 mLocalDeviceIdleController
962                         = LocalServices.getService(DeviceIdleController.LocalService.class);
963                 // Create the "runners".
964                 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
965                     mActiveServices.add(
966                             new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
967                                     getContext().getMainLooper()));
968                 }
969                 // Attach jobs to their controllers.
970                 mJobs.forEachJob(new JobStatusFunctor() {
971                     @Override
972                     public void process(JobStatus job) {
973                         for (int controller = 0; controller < mControllers.size(); controller++) {
974                             final StateController sc = mControllers.get(controller);
975                             sc.maybeStartTrackingJobLocked(job, null);
976                         }
977                     }
978                 });
979                 // GO GO GO!
980                 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
981             }
982         }
983     }
984 
985     /**
986      * Called when we have a job status object that we need to insert in our
987      * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
988      * about.
989      */
startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob)990     private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
991         if (!jobStatus.isPreparedLocked()) {
992             Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
993         }
994         jobStatus.enqueueTime = SystemClock.elapsedRealtime();
995         final boolean update = mJobs.add(jobStatus);
996         if (mReadyToRock) {
997             for (int i = 0; i < mControllers.size(); i++) {
998                 StateController controller = mControllers.get(i);
999                 if (update) {
1000                     controller.maybeStopTrackingJobLocked(jobStatus, null, true);
1001                 }
1002                 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
1003             }
1004         }
1005     }
1006 
1007     /**
1008      * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1009      * object removed.
1010      */
stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob, boolean writeBack)1011     private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
1012             boolean writeBack) {
1013         // Deal with any remaining work items in the old job.
1014         jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
1015 
1016         // Remove from store as well as controllers.
1017         final boolean removed = mJobs.remove(jobStatus, writeBack);
1018         if (removed && mReadyToRock) {
1019             for (int i=0; i<mControllers.size(); i++) {
1020                 StateController controller = mControllers.get(i);
1021                 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
1022             }
1023         }
1024         return removed;
1025     }
1026 
stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason)1027     private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
1028         for (int i=0; i<mActiveServices.size(); i++) {
1029             JobServiceContext jsc = mActiveServices.get(i);
1030             final JobStatus executing = jsc.getRunningJobLocked();
1031             if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
1032                 jsc.cancelExecutingJobLocked(reason, debugReason);
1033                 return true;
1034             }
1035         }
1036         return false;
1037     }
1038 
1039     /**
1040      * @param job JobStatus we are querying against.
1041      * @return Whether or not the job represented by the status object is currently being run or
1042      * is pending.
1043      */
isCurrentlyActiveLocked(JobStatus job)1044     private boolean isCurrentlyActiveLocked(JobStatus job) {
1045         for (int i=0; i<mActiveServices.size(); i++) {
1046             JobServiceContext serviceContext = mActiveServices.get(i);
1047             final JobStatus running = serviceContext.getRunningJobLocked();
1048             if (running != null && running.matches(job.getUid(), job.getJobId())) {
1049                 return true;
1050             }
1051         }
1052         return false;
1053     }
1054 
noteJobsPending(List<JobStatus> jobs)1055     void noteJobsPending(List<JobStatus> jobs) {
1056         for (int i = jobs.size() - 1; i >= 0; i--) {
1057             JobStatus job = jobs.get(i);
1058             mJobPackageTracker.notePending(job);
1059         }
1060     }
1061 
noteJobsNonpending(List<JobStatus> jobs)1062     void noteJobsNonpending(List<JobStatus> jobs) {
1063         for (int i = jobs.size() - 1; i >= 0; i--) {
1064             JobStatus job = jobs.get(i);
1065             mJobPackageTracker.noteNonpending(job);
1066         }
1067     }
1068 
1069     /**
1070      * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1071      * specify an override deadline on a failed job (the failed job will run even though it's not
1072      * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1073      * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1074      *
1075      * @param failureToReschedule Provided job status that we will reschedule.
1076      * @return A newly instantiated JobStatus with the same constraints as the last job except
1077      * with adjusted timing constraints.
1078      *
1079      * @see #maybeQueueReadyJobsForExecutionLocked
1080      */
getRescheduleJobForFailureLocked(JobStatus failureToReschedule)1081     private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
1082         final long elapsedNowMillis = SystemClock.elapsedRealtime();
1083         final JobInfo job = failureToReschedule.getJob();
1084 
1085         final long initialBackoffMillis = job.getInitialBackoffMillis();
1086         final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1087         long delayMillis;
1088 
1089         if (failureToReschedule.hasWorkLocked()) {
1090             if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1091                 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1092                         + backoffAttempts + " > work limit "
1093                         + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1094                 return null;
1095             }
1096         } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1097             Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1098                     + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1099             return null;
1100         }
1101 
1102         switch (job.getBackoffPolicy()) {
1103             case JobInfo.BACKOFF_POLICY_LINEAR: {
1104                 long backoff = initialBackoffMillis;
1105                 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1106                     backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1107                 }
1108                 delayMillis = backoff * backoffAttempts;
1109             } break;
1110             default:
1111                 if (DEBUG) {
1112                     Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1113                 }
1114             case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1115                 long backoff = initialBackoffMillis;
1116                 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1117                     backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1118                 }
1119                 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1120             } break;
1121         }
1122         delayMillis =
1123                 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
1124         JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
1125                 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
1126         for (int ic=0; ic<mControllers.size(); ic++) {
1127             StateController controller = mControllers.get(ic);
1128             controller.rescheduleForFailureLocked(newJob, failureToReschedule);
1129         }
1130         return newJob;
1131     }
1132 
1133     /**
1134      * Called after a periodic has executed so we can reschedule it. We take the last execution
1135      * time of the job to be the time of completion (i.e. the time at which this function is
1136      * called).
1137      * This could be inaccurate b/c the job can run for as long as
1138      * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1139      * to underscheduling at least, rather than if we had taken the last execution time to be the
1140      * start of the execution.
1141      * @return A new job representing the execution criteria for this instantiation of the
1142      * recurring job.
1143      */
getRescheduleJobForPeriodic(JobStatus periodicToReschedule)1144     private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1145         final long elapsedNow = SystemClock.elapsedRealtime();
1146         // Compute how much of the period is remaining.
1147         long runEarly = 0L;
1148 
1149         // If this periodic was rescheduled it won't have a deadline.
1150         if (periodicToReschedule.hasDeadlineConstraint()) {
1151             runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1152         }
1153         long flex = periodicToReschedule.getJob().getFlexMillis();
1154         long period = periodicToReschedule.getJob().getIntervalMillis();
1155         long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1156         long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
1157 
1158         if (DEBUG) {
1159             Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1160                     newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1161         }
1162         return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1163                 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1164     }
1165 
1166     // JobCompletedListener implementations.
1167 
1168     /**
1169      * A job just finished executing. We fetch the
1170      * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1171      * whether we want to reschedule we readd it to the controllers.
1172      * @param jobStatus Completed job.
1173      * @param needsReschedule Whether the implementing class should reschedule this job.
1174      */
1175     @Override
onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule)1176     public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
1177         if (DEBUG) {
1178             Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1179         }
1180 
1181         // If the job wants to be rescheduled, we first need to make the next upcoming
1182         // job so we can transfer any appropriate state over from the previous job when
1183         // we stop it.
1184         final JobStatus rescheduledJob = needsReschedule
1185                 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1186 
1187         // Do not write back immediately if this is a periodic job. The job may get lost if system
1188         // shuts down before it is added back.
1189         if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
1190             if (DEBUG) {
1191                 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
1192             }
1193             // We still want to check for jobs to execute, because this job may have
1194             // scheduled a new job under the same job id, and now we can run it.
1195             mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1196             return;
1197         }
1198 
1199         if (rescheduledJob != null) {
1200             try {
1201                 rescheduledJob.prepareLocked(ActivityManager.getService());
1202             } catch (SecurityException e) {
1203                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
1204             }
1205             startTrackingJobLocked(rescheduledJob, jobStatus);
1206         } else if (jobStatus.getJob().isPeriodic()) {
1207             JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
1208             try {
1209                 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
1210             } catch (SecurityException e) {
1211                 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1212             }
1213             startTrackingJobLocked(rescheduledPeriodic, jobStatus);
1214         }
1215         jobStatus.unprepareLocked(ActivityManager.getService());
1216         reportActiveLocked();
1217         mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1218     }
1219 
1220     // StateChangedListener implementations.
1221 
1222     /**
1223      * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1224      * some controller's state has changed, so as to run through the list of jobs and start/stop
1225      * any that are eligible.
1226      */
1227     @Override
onControllerStateChanged()1228     public void onControllerStateChanged() {
1229         mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1230     }
1231 
1232     @Override
onRunJobNow(JobStatus jobStatus)1233     public void onRunJobNow(JobStatus jobStatus) {
1234         mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1235     }
1236 
1237     final private class JobHandler extends Handler {
1238 
JobHandler(Looper looper)1239         public JobHandler(Looper looper) {
1240             super(looper);
1241         }
1242 
1243         @Override
handleMessage(Message message)1244         public void handleMessage(Message message) {
1245             synchronized (mLock) {
1246                 if (!mReadyToRock) {
1247                     return;
1248                 }
1249                 switch (message.what) {
1250                     case MSG_JOB_EXPIRED: {
1251                         JobStatus runNow = (JobStatus) message.obj;
1252                         // runNow can be null, which is a controller's way of indicating that its
1253                         // state is such that all ready jobs should be run immediately.
1254                         if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
1255                             mJobPackageTracker.notePending(runNow);
1256                             addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
1257                         } else {
1258                             queueReadyJobsForExecutionLocked();
1259                         }
1260                     } break;
1261                     case MSG_CHECK_JOB:
1262                         if (mReportedActive) {
1263                             // if jobs are currently being run, queue all ready jobs for execution.
1264                             queueReadyJobsForExecutionLocked();
1265                         } else {
1266                             // Check the list of jobs and run some of them if we feel inclined.
1267                             maybeQueueReadyJobsForExecutionLocked();
1268                         }
1269                         break;
1270                     case MSG_CHECK_JOB_GREEDY:
1271                         queueReadyJobsForExecutionLocked();
1272                         break;
1273                     case MSG_STOP_JOB:
1274                         cancelJobImplLocked((JobStatus) message.obj, null,
1275                                 "app no longer allowed to run");
1276                         break;
1277                 }
1278                 maybeRunPendingJobsLocked();
1279                 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1280                 removeMessages(MSG_CHECK_JOB);
1281             }
1282         }
1283     }
1284 
stopNonReadyActiveJobsLocked()1285     private void stopNonReadyActiveJobsLocked() {
1286         for (int i=0; i<mActiveServices.size(); i++) {
1287             JobServiceContext serviceContext = mActiveServices.get(i);
1288             final JobStatus running = serviceContext.getRunningJobLocked();
1289             if (running != null && !running.isReady()) {
1290                 serviceContext.cancelExecutingJobLocked(
1291                         JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1292                         "cancelled due to unsatisfied constraints");
1293             }
1294         }
1295     }
1296 
1297     /**
1298      * Run through list of jobs and execute all possible - at least one is expired so we do
1299      * as many as we can.
1300      */
queueReadyJobsForExecutionLocked()1301     private void queueReadyJobsForExecutionLocked() {
1302         if (DEBUG) {
1303             Slog.d(TAG, "queuing all ready jobs for execution:");
1304         }
1305         noteJobsNonpending(mPendingJobs);
1306         mPendingJobs.clear();
1307         stopNonReadyActiveJobsLocked();
1308         mJobs.forEachJob(mReadyQueueFunctor);
1309         mReadyQueueFunctor.postProcess();
1310 
1311         if (DEBUG) {
1312             final int queuedJobs = mPendingJobs.size();
1313             if (queuedJobs == 0) {
1314                 Slog.d(TAG, "No jobs pending.");
1315             } else {
1316                 Slog.d(TAG, queuedJobs + " jobs queued.");
1317             }
1318         }
1319     }
1320 
1321     final class ReadyJobQueueFunctor implements JobStatusFunctor {
1322         ArrayList<JobStatus> newReadyJobs;
1323 
1324         @Override
process(JobStatus job)1325         public void process(JobStatus job) {
1326             if (isReadyToBeExecutedLocked(job)) {
1327                 if (DEBUG) {
1328                     Slog.d(TAG, "    queued " + job.toShortString());
1329                 }
1330                 if (newReadyJobs == null) {
1331                     newReadyJobs = new ArrayList<JobStatus>();
1332                 }
1333                 newReadyJobs.add(job);
1334             }
1335         }
1336 
postProcess()1337         public void postProcess() {
1338             if (newReadyJobs != null) {
1339                 noteJobsPending(newReadyJobs);
1340                 mPendingJobs.addAll(newReadyJobs);
1341                 if (mPendingJobs.size() > 1) {
1342                     mPendingJobs.sort(mEnqueueTimeComparator);
1343                 }
1344             }
1345             newReadyJobs = null;
1346         }
1347     }
1348     private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1349 
1350     /**
1351      * The state of at least one job has changed. Here is where we could enforce various
1352      * policies on when we want to execute jobs.
1353      * Right now the policy is such:
1354      * If >1 of the ready jobs is idle mode we send all of them off
1355      * if more than 2 network connectivity jobs are ready we send them all off.
1356      * If more than 4 jobs total are ready we send them all off.
1357      * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1358      */
1359     final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1360         int chargingCount;
1361         int batteryNotLowCount;
1362         int storageNotLowCount;
1363         int idleCount;
1364         int backoffCount;
1365         int connectivityCount;
1366         int contentCount;
1367         List<JobStatus> runnableJobs;
1368 
MaybeReadyJobQueueFunctor()1369         public MaybeReadyJobQueueFunctor() {
1370             reset();
1371         }
1372 
1373         // Functor method invoked for each job via JobStore.forEachJob()
1374         @Override
process(JobStatus job)1375         public void process(JobStatus job) {
1376             if (isReadyToBeExecutedLocked(job)) {
1377                 try {
1378                     if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1379                             job.getJob().getService().getPackageName())) {
1380                         Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1381                                 + job.getJob().toString() + " -- package not allowed to start");
1382                         mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1383                         return;
1384                     }
1385                 } catch (RemoteException e) {
1386                 }
1387                 if (job.getNumFailures() > 0) {
1388                     backoffCount++;
1389                 }
1390                 if (job.hasIdleConstraint()) {
1391                     idleCount++;
1392                 }
1393                 if (job.hasConnectivityConstraint()) {
1394                     connectivityCount++;
1395                 }
1396                 if (job.hasChargingConstraint()) {
1397                     chargingCount++;
1398                 }
1399                 if (job.hasBatteryNotLowConstraint()) {
1400                     batteryNotLowCount++;
1401                 }
1402                 if (job.hasStorageNotLowConstraint()) {
1403                     storageNotLowCount++;
1404                 }
1405                 if (job.hasContentTriggerConstraint()) {
1406                     contentCount++;
1407                 }
1408                 if (runnableJobs == null) {
1409                     runnableJobs = new ArrayList<>();
1410                 }
1411                 runnableJobs.add(job);
1412             }
1413         }
1414 
postProcess()1415         public void postProcess() {
1416             if (backoffCount > 0 ||
1417                     idleCount >= mConstants.MIN_IDLE_COUNT ||
1418                     connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1419                     chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1420                     batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1421                     storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1422                     contentCount >= mConstants.MIN_CONTENT_COUNT ||
1423                     (runnableJobs != null
1424                             && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1425                 if (DEBUG) {
1426                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1427                 }
1428                 noteJobsPending(runnableJobs);
1429                 mPendingJobs.addAll(runnableJobs);
1430                 if (mPendingJobs.size() > 1) {
1431                     mPendingJobs.sort(mEnqueueTimeComparator);
1432                 }
1433             } else {
1434                 if (DEBUG) {
1435                     Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1436                 }
1437             }
1438 
1439             // Be ready for next time
1440             reset();
1441         }
1442 
reset()1443         private void reset() {
1444             chargingCount = 0;
1445             idleCount =  0;
1446             backoffCount = 0;
1447             connectivityCount = 0;
1448             batteryNotLowCount = 0;
1449             storageNotLowCount = 0;
1450             contentCount = 0;
1451             runnableJobs = null;
1452         }
1453     }
1454     private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1455 
maybeQueueReadyJobsForExecutionLocked()1456     private void maybeQueueReadyJobsForExecutionLocked() {
1457         if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1458 
1459         noteJobsNonpending(mPendingJobs);
1460         mPendingJobs.clear();
1461         stopNonReadyActiveJobsLocked();
1462         mJobs.forEachJob(mMaybeQueueFunctor);
1463         mMaybeQueueFunctor.postProcess();
1464     }
1465 
1466     /**
1467      * Criteria for moving a job into the pending queue:
1468      *      - It's ready.
1469      *      - It's not pending.
1470      *      - It's not already running on a JSC.
1471      *      - The user that requested the job is running.
1472      *      - The component is enabled and runnable.
1473      */
isReadyToBeExecutedLocked(JobStatus job)1474     private boolean isReadyToBeExecutedLocked(JobStatus job) {
1475         final boolean jobReady = job.isReady();
1476 
1477         if (DEBUG) {
1478             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1479                     + " ready=" + jobReady);
1480         }
1481 
1482         // This is a condition that is very likely to be false (most jobs that are
1483         // scheduled are sitting there, not ready yet) and very cheap to check (just
1484         // a few conditions on data in JobStatus).
1485         if (!jobReady) {
1486             return false;
1487         }
1488 
1489         final boolean jobExists = mJobs.containsJob(job);
1490 
1491         final int userId = job.getUserId();
1492         final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1493 
1494         if (DEBUG) {
1495             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1496                     + " exists=" + jobExists + " userStarted=" + userStarted);
1497         }
1498 
1499         // These are also fairly cheap to check, though they typically will not
1500         // be conditions we fail.
1501         if (!jobExists || !userStarted) {
1502             return false;
1503         }
1504 
1505         final boolean jobPending = mPendingJobs.contains(job);
1506         final boolean jobActive = isCurrentlyActiveLocked(job);
1507 
1508         if (DEBUG) {
1509             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1510                     + " pending=" + jobPending + " active=" + jobActive);
1511         }
1512 
1513         // These can be a little more expensive (especially jobActive, since we need to
1514         // go through the array of all potentially active jobs), so we are doing them
1515         // later...  but still before checking with the package manager!
1516         if (jobPending || jobActive) {
1517             return false;
1518         }
1519 
1520         final boolean componentPresent;
1521         try {
1522             componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1523                     job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1524                     userId) != null);
1525         } catch (RemoteException e) {
1526             throw e.rethrowAsRuntimeException();
1527         }
1528 
1529         if (DEBUG) {
1530             Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1531                     + " componentPresent=" + componentPresent);
1532         }
1533 
1534         // Everything else checked out so far, so this is the final yes/no check
1535         return componentPresent;
1536     }
1537 
1538     /**
1539      * Reconcile jobs in the pending queue against available execution contexts.
1540      * A controller can force a job into the pending queue even if it's already running, but
1541      * here is where we decide whether to actually execute it.
1542      */
maybeRunPendingJobsLocked()1543     private void maybeRunPendingJobsLocked() {
1544         if (DEBUG) {
1545             Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1546         }
1547         assignJobsToContextsLocked();
1548         reportActiveLocked();
1549     }
1550 
adjustJobPriority(int curPriority, JobStatus job)1551     private int adjustJobPriority(int curPriority, JobStatus job) {
1552         if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1553             float factor = mJobPackageTracker.getLoadFactor(job);
1554             if (factor >= mConstants.HEAVY_USE_FACTOR) {
1555                 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
1556             } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
1557                 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1558             }
1559         }
1560         return curPriority;
1561     }
1562 
evaluateJobPriorityLocked(JobStatus job)1563     private int evaluateJobPriorityLocked(JobStatus job) {
1564         int priority = job.getPriority();
1565         if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
1566             return adjustJobPriority(priority, job);
1567         }
1568         int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1569         if (override != 0) {
1570             return adjustJobPriority(override, job);
1571         }
1572         return adjustJobPriority(priority, job);
1573     }
1574 
1575     /**
1576      * Takes jobs from pending queue and runs them on available contexts.
1577      * If no contexts are available, preempts lower priority jobs to
1578      * run higher priority ones.
1579      * Lock on mJobs before calling this function.
1580      */
assignJobsToContextsLocked()1581     private void assignJobsToContextsLocked() {
1582         if (DEBUG) {
1583             Slog.d(TAG, printPendingQueue());
1584         }
1585 
1586         int memLevel;
1587         try {
1588             memLevel = ActivityManager.getService().getMemoryTrimLevel();
1589         } catch (RemoteException e) {
1590             memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1591         }
1592         switch (memLevel) {
1593             case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
1594                 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
1595                 break;
1596             case ProcessStats.ADJ_MEM_FACTOR_LOW:
1597                 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
1598                 break;
1599             case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
1600                 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
1601                 break;
1602             default:
1603                 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
1604                 break;
1605         }
1606 
1607         JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1608         boolean[] act = mTmpAssignAct;
1609         int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1610         int numActive = 0;
1611         int numForeground = 0;
1612         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1613             final JobServiceContext js = mActiveServices.get(i);
1614             final JobStatus status = js.getRunningJobLocked();
1615             if ((contextIdToJobMap[i] = status) != null) {
1616                 numActive++;
1617                 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1618                     numForeground++;
1619                 }
1620             }
1621             act[i] = false;
1622             preferredUidForContext[i] = js.getPreferredUid();
1623         }
1624         if (DEBUG) {
1625             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1626         }
1627         for (int i=0; i<mPendingJobs.size(); i++) {
1628             JobStatus nextPending = mPendingJobs.get(i);
1629 
1630             // If job is already running, go to next job.
1631             int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1632             if (jobRunningContext != -1) {
1633                 continue;
1634             }
1635 
1636             final int priority = evaluateJobPriorityLocked(nextPending);
1637             nextPending.lastEvaluatedPriority = priority;
1638 
1639             // Find a context for nextPending. The context should be available OR
1640             // it should have lowest priority among all running jobs
1641             // (sharing the same Uid as nextPending)
1642             int minPriority = Integer.MAX_VALUE;
1643             int minPriorityContextId = -1;
1644             for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1645                 JobStatus job = contextIdToJobMap[j];
1646                 int preferredUid = preferredUidForContext[j];
1647                 if (job == null) {
1648                     if ((numActive < mMaxActiveJobs ||
1649                             (priority >= JobInfo.PRIORITY_TOP_APP &&
1650                                     numForeground < mConstants.FG_JOB_COUNT)) &&
1651                             (preferredUid == nextPending.getUid() ||
1652                                     preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1653                         // This slot is free, and we haven't yet hit the limit on
1654                         // concurrent jobs...  we can just throw the job in to here.
1655                         minPriorityContextId = j;
1656                         break;
1657                     }
1658                     // No job on this context, but nextPending can't run here because
1659                     // the context has a preferred Uid or we have reached the limit on
1660                     // concurrent jobs.
1661                     continue;
1662                 }
1663                 if (job.getUid() != nextPending.getUid()) {
1664                     continue;
1665                 }
1666                 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
1667                     continue;
1668                 }
1669                 if (minPriority > nextPending.lastEvaluatedPriority) {
1670                     minPriority = nextPending.lastEvaluatedPriority;
1671                     minPriorityContextId = j;
1672                 }
1673             }
1674             if (minPriorityContextId != -1) {
1675                 contextIdToJobMap[minPriorityContextId] = nextPending;
1676                 act[minPriorityContextId] = true;
1677                 numActive++;
1678                 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1679                     numForeground++;
1680                 }
1681             }
1682         }
1683         if (DEBUG) {
1684             Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1685         }
1686         mJobPackageTracker.noteConcurrency(numActive, numForeground);
1687         for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1688             boolean preservePreferredUid = false;
1689             if (act[i]) {
1690                 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
1691                 if (js != null) {
1692                     if (DEBUG) {
1693                         Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
1694                     }
1695                     // preferredUid will be set to uid of currently running job.
1696                     mActiveServices.get(i).preemptExecutingJobLocked();
1697                     preservePreferredUid = true;
1698                 } else {
1699                     final JobStatus pendingJob = contextIdToJobMap[i];
1700                     if (DEBUG) {
1701                         Slog.d(TAG, "About to run job on context "
1702                                 + String.valueOf(i) + ", job: " + pendingJob);
1703                     }
1704                     for (int ic=0; ic<mControllers.size(); ic++) {
1705                         mControllers.get(ic).prepareForExecutionLocked(pendingJob);
1706                     }
1707                     if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1708                         Slog.d(TAG, "Error executing " + pendingJob);
1709                     }
1710                     if (mPendingJobs.remove(pendingJob)) {
1711                         mJobPackageTracker.noteNonpending(pendingJob);
1712                     }
1713                 }
1714             }
1715             if (!preservePreferredUid) {
1716                 mActiveServices.get(i).clearPreferredUid();
1717             }
1718         }
1719     }
1720 
findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map)1721     int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1722         for (int i=0; i<map.length; i++) {
1723             if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1724                 return i;
1725             }
1726         }
1727         return -1;
1728     }
1729 
1730     final class LocalService implements JobSchedulerInternal {
1731 
1732         /**
1733          * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1734          * jobs are always considered pending.
1735          */
1736         @Override
getSystemScheduledPendingJobs()1737         public List<JobInfo> getSystemScheduledPendingJobs() {
1738             synchronized (mLock) {
1739                 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1740                 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1741                     @Override
1742                     public void process(JobStatus job) {
1743                         if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1744                             pendingJobs.add(job.getJob());
1745                         }
1746                     }
1747                 });
1748                 return pendingJobs;
1749             }
1750         }
1751 
1752         @Override
addBackingUpUid(int uid)1753         public void addBackingUpUid(int uid) {
1754             synchronized (mLock) {
1755                 // No need to actually do anything here, since for a full backup the
1756                 // activity manager will kill the process which will kill the job (and
1757                 // cause it to restart, but now it can't run).
1758                 mBackingUpUids.put(uid, uid);
1759             }
1760         }
1761 
1762         @Override
removeBackingUpUid(int uid)1763         public void removeBackingUpUid(int uid) {
1764             synchronized (mLock) {
1765                 mBackingUpUids.delete(uid);
1766                 // If there are any jobs for this uid, we need to rebuild the pending list
1767                 // in case they are now ready to run.
1768                 if (mJobs.countJobsForUid(uid) > 0) {
1769                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1770                 }
1771             }
1772         }
1773 
1774         @Override
clearAllBackingUpUids()1775         public void clearAllBackingUpUids() {
1776             synchronized (mLock) {
1777                 if (mBackingUpUids.size() > 0) {
1778                     mBackingUpUids.clear();
1779                     mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1780                 }
1781             }
1782         }
1783     }
1784 
1785     /**
1786      * Binder stub trampoline implementation
1787      */
1788     final class JobSchedulerStub extends IJobScheduler.Stub {
1789         /** Cache determination of whether a given app can persist jobs
1790          * key is uid of the calling app; value is undetermined/true/false
1791          */
1792         private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1793 
1794         // Enforce that only the app itself (or shared uid participant) can schedule a
1795         // job that runs one of the app's services, as well as verifying that the
1796         // named service properly requires the BIND_JOB_SERVICE permission
enforceValidJobRequest(int uid, JobInfo job)1797         private void enforceValidJobRequest(int uid, JobInfo job) {
1798             final IPackageManager pm = AppGlobals.getPackageManager();
1799             final ComponentName service = job.getService();
1800             try {
1801                 ServiceInfo si = pm.getServiceInfo(service,
1802                         PackageManager.MATCH_DIRECT_BOOT_AWARE
1803                                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
1804                         UserHandle.getUserId(uid));
1805                 if (si == null) {
1806                     throw new IllegalArgumentException("No such service " + service);
1807                 }
1808                 if (si.applicationInfo.uid != uid) {
1809                     throw new IllegalArgumentException("uid " + uid +
1810                             " cannot schedule job in " + service.getPackageName());
1811                 }
1812                 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1813                     throw new IllegalArgumentException("Scheduled service " + service
1814                             + " does not require android.permission.BIND_JOB_SERVICE permission");
1815                 }
1816             } catch (RemoteException e) {
1817                 // Can't happen; the Package Manager is in this same process
1818             }
1819         }
1820 
canPersistJobs(int pid, int uid)1821         private boolean canPersistJobs(int pid, int uid) {
1822             // If we get this far we're good to go; all we need to do now is check
1823             // whether the app is allowed to persist its scheduled work.
1824             final boolean canPersist;
1825             synchronized (mPersistCache) {
1826                 Boolean cached = mPersistCache.get(uid);
1827                 if (cached != null) {
1828                     canPersist = cached.booleanValue();
1829                 } else {
1830                     // Persisting jobs is tantamount to running at boot, so we permit
1831                     // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1832                     // permission
1833                     int result = getContext().checkPermission(
1834                             android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1835                     canPersist = (result == PackageManager.PERMISSION_GRANTED);
1836                     mPersistCache.put(uid, canPersist);
1837                 }
1838             }
1839             return canPersist;
1840         }
1841 
1842         // IJobScheduler implementation
1843         @Override
schedule(JobInfo job)1844         public int schedule(JobInfo job) throws RemoteException {
1845             if (DEBUG) {
1846                 Slog.d(TAG, "Scheduling job: " + job.toString());
1847             }
1848             final int pid = Binder.getCallingPid();
1849             final int uid = Binder.getCallingUid();
1850 
1851             enforceValidJobRequest(uid, job);
1852             if (job.isPersisted()) {
1853                 if (!canPersistJobs(pid, uid)) {
1854                     throw new IllegalArgumentException("Error: requested job be persisted without"
1855                             + " holding RECEIVE_BOOT_COMPLETED permission.");
1856                 }
1857             }
1858 
1859             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1860                 getContext().enforceCallingOrSelfPermission(
1861                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1862             }
1863 
1864             long ident = Binder.clearCallingIdentity();
1865             try {
1866                 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1867             } finally {
1868                 Binder.restoreCallingIdentity(ident);
1869             }
1870         }
1871 
1872         // IJobScheduler implementation
1873         @Override
enqueue(JobInfo job, JobWorkItem work)1874         public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1875             if (DEBUG) {
1876                 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1877             }
1878             final int pid = Binder.getCallingPid();
1879             final int uid = Binder.getCallingUid();
1880 
1881             enforceValidJobRequest(uid, job);
1882             if (job.isPersisted()) {
1883                 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1884             }
1885             if (work == null) {
1886                 throw new NullPointerException("work is null");
1887             }
1888 
1889             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1890                 getContext().enforceCallingOrSelfPermission(
1891                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1892             }
1893 
1894             long ident = Binder.clearCallingIdentity();
1895             try {
1896                 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
1897             } finally {
1898                 Binder.restoreCallingIdentity(ident);
1899             }
1900         }
1901 
1902         @Override
scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)1903         public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
1904                 throws RemoteException {
1905             final int callerUid = Binder.getCallingUid();
1906             if (DEBUG) {
1907                 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1908                         + " on behalf of " + packageName);
1909             }
1910 
1911             if (packageName == null) {
1912                 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
1913             }
1914 
1915             int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1916                     android.Manifest.permission.UPDATE_DEVICE_STATS);
1917             if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1918                 throw new SecurityException("Caller uid " + callerUid
1919                         + " not permitted to schedule jobs for other apps");
1920             }
1921 
1922             if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1923                 getContext().enforceCallingOrSelfPermission(
1924                         android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1925             }
1926 
1927             long ident = Binder.clearCallingIdentity();
1928             try {
1929                 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
1930                         packageName, userId, tag);
1931             } finally {
1932                 Binder.restoreCallingIdentity(ident);
1933             }
1934         }
1935 
1936         @Override
getAllPendingJobs()1937         public List<JobInfo> getAllPendingJobs() throws RemoteException {
1938             final int uid = Binder.getCallingUid();
1939 
1940             long ident = Binder.clearCallingIdentity();
1941             try {
1942                 return JobSchedulerService.this.getPendingJobs(uid);
1943             } finally {
1944                 Binder.restoreCallingIdentity(ident);
1945             }
1946         }
1947 
1948         @Override
getPendingJob(int jobId)1949         public JobInfo getPendingJob(int jobId) throws RemoteException {
1950             final int uid = Binder.getCallingUid();
1951 
1952             long ident = Binder.clearCallingIdentity();
1953             try {
1954                 return JobSchedulerService.this.getPendingJob(uid, jobId);
1955             } finally {
1956                 Binder.restoreCallingIdentity(ident);
1957             }
1958         }
1959 
1960         @Override
cancelAll()1961         public void cancelAll() throws RemoteException {
1962             final int uid = Binder.getCallingUid();
1963 
1964             long ident = Binder.clearCallingIdentity();
1965             try {
1966                 JobSchedulerService.this.cancelJobsForUid(uid, "cancelAll() called by app");
1967             } finally {
1968                 Binder.restoreCallingIdentity(ident);
1969             }
1970         }
1971 
1972         @Override
cancel(int jobId)1973         public void cancel(int jobId) throws RemoteException {
1974             final int uid = Binder.getCallingUid();
1975 
1976             long ident = Binder.clearCallingIdentity();
1977             try {
1978                 JobSchedulerService.this.cancelJob(uid, jobId);
1979             } finally {
1980                 Binder.restoreCallingIdentity(ident);
1981             }
1982         }
1983 
1984         /**
1985          * "dumpsys" infrastructure
1986          */
1987         @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1988         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1989             if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
1990 
1991             long identityToken = Binder.clearCallingIdentity();
1992             try {
1993                 JobSchedulerService.this.dumpInternal(pw, args);
1994             } finally {
1995                 Binder.restoreCallingIdentity(identityToken);
1996             }
1997         }
1998 
1999         @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)2000         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
2001                 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
2002                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
2003                         this, in, out, err, args, callback, resultReceiver);
2004         }
2005     };
2006 
2007     // Shell command infrastructure: run the given job immediately
executeRunCommand(String pkgName, int userId, int jobId, boolean force)2008     int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2009         if (DEBUG) {
2010             Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2011                     + " " + jobId + " f=" + force);
2012         }
2013 
2014         try {
2015             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2016                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2017             if (uid < 0) {
2018                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2019             }
2020 
2021             synchronized (mLock) {
2022                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2023                 if (js == null) {
2024                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2025                 }
2026 
2027                 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2028                 if (!js.isConstraintsSatisfied()) {
2029                     js.overrideState = 0;
2030                     return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2031                 }
2032 
2033                 queueReadyJobsForExecutionLocked();
2034                 maybeRunPendingJobsLocked();
2035             }
2036         } catch (RemoteException e) {
2037             // can't happen
2038         }
2039         return 0;
2040     }
2041 
2042     // Shell command infrastructure: immediately timeout currently executing jobs
executeTimeoutCommand(PrintWriter pw, String pkgName, int userId, boolean hasJobId, int jobId)2043     int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2044             boolean hasJobId, int jobId) {
2045         if (DEBUG) {
2046             Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2047         }
2048 
2049         synchronized (mLock) {
2050             boolean foundSome = false;
2051             for (int i=0; i<mActiveServices.size(); i++) {
2052                 mActiveServices.get(i).timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId);
2053             }
2054             if (!foundSome) {
2055                 pw.println("No matching executing jobs found.");
2056             }
2057         }
2058         return 0;
2059     }
2060 
setMonitorBattery(boolean enabled)2061     void setMonitorBattery(boolean enabled) {
2062         synchronized (mLock) {
2063             if (mBatteryController != null) {
2064                 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2065             }
2066         }
2067     }
2068 
getBatterySeq()2069     int getBatterySeq() {
2070         synchronized (mLock) {
2071             return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2072         }
2073     }
2074 
getBatteryCharging()2075     boolean getBatteryCharging() {
2076         synchronized (mLock) {
2077             return mBatteryController != null
2078                     ? mBatteryController.getTracker().isOnStablePower() : false;
2079         }
2080     }
2081 
getBatteryNotLow()2082     boolean getBatteryNotLow() {
2083         synchronized (mLock) {
2084             return mBatteryController != null
2085                     ? mBatteryController.getTracker().isBatteryNotLow() : false;
2086         }
2087     }
2088 
getStorageSeq()2089     int getStorageSeq() {
2090         synchronized (mLock) {
2091             return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2092         }
2093     }
2094 
getStorageNotLow()2095     boolean getStorageNotLow() {
2096         synchronized (mLock) {
2097             return mStorageController != null
2098                     ? mStorageController.getTracker().isStorageNotLow() : false;
2099         }
2100     }
2101 
getJobState(PrintWriter pw, String pkgName, int userId, int jobId)2102     int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2103         try {
2104             final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2105                     userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2106             if (uid < 0) {
2107                 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2108                 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2109             }
2110 
2111             synchronized (mLock) {
2112                 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2113                 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2114                 if (js == null) {
2115                     pw.print("unknown("); UserHandle.formatUid(pw, uid);
2116                     pw.print("/jid"); pw.print(jobId); pw.println(")");
2117                     return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2118                 }
2119 
2120                 boolean printed = false;
2121                 if (mPendingJobs.contains(js)) {
2122                     pw.print("pending");
2123                     printed = true;
2124                 }
2125                 if (isCurrentlyActiveLocked(js)) {
2126                     if (printed) {
2127                         pw.print(" ");
2128                     }
2129                     printed = true;
2130                     pw.println("active");
2131                 }
2132                 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2133                     if (printed) {
2134                         pw.print(" ");
2135                     }
2136                     printed = true;
2137                     pw.println("user-stopped");
2138                 }
2139                 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2140                     if (printed) {
2141                         pw.print(" ");
2142                     }
2143                     printed = true;
2144                     pw.println("backing-up");
2145                 }
2146                 boolean componentPresent = false;
2147                 try {
2148                     componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2149                             js.getServiceComponent(),
2150                             PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2151                             js.getUserId()) != null);
2152                 } catch (RemoteException e) {
2153                 }
2154                 if (!componentPresent) {
2155                     if (printed) {
2156                         pw.print(" ");
2157                     }
2158                     printed = true;
2159                     pw.println("no-component");
2160                 }
2161                 if (js.isReady()) {
2162                     if (printed) {
2163                         pw.print(" ");
2164                     }
2165                     printed = true;
2166                     pw.println("ready");
2167                 }
2168                 if (!printed) {
2169                     pw.print("waiting");
2170                 }
2171                 pw.println();
2172             }
2173         } catch (RemoteException e) {
2174             // can't happen
2175         }
2176         return 0;
2177     }
2178 
printContextIdToJobMap(JobStatus[] map, String initial)2179     private String printContextIdToJobMap(JobStatus[] map, String initial) {
2180         StringBuilder s = new StringBuilder(initial + ": ");
2181         for (int i=0; i<map.length; i++) {
2182             s.append("(")
2183                     .append(map[i] == null? -1: map[i].getJobId())
2184                     .append(map[i] == null? -1: map[i].getUid())
2185                     .append(")" );
2186         }
2187         return s.toString();
2188     }
2189 
printPendingQueue()2190     private String printPendingQueue() {
2191         StringBuilder s = new StringBuilder("Pending queue: ");
2192         Iterator<JobStatus> it = mPendingJobs.iterator();
2193         while (it.hasNext()) {
2194             JobStatus js = it.next();
2195             s.append("(")
2196                     .append(js.getJob().getId())
2197                     .append(", ")
2198                     .append(js.getUid())
2199                     .append(") ");
2200         }
2201         return s.toString();
2202     }
2203 
dumpHelp(PrintWriter pw)2204     static void dumpHelp(PrintWriter pw) {
2205         pw.println("Job Scheduler (jobscheduler) dump options:");
2206         pw.println("  [-h] [package] ...");
2207         pw.println("    -h: print this help");
2208         pw.println("  [package] is an optional package name to limit the output to.");
2209     }
2210 
dumpInternal(final PrintWriter pw, String[] args)2211     void dumpInternal(final PrintWriter pw, String[] args) {
2212         int filterUid = -1;
2213         if (!ArrayUtils.isEmpty(args)) {
2214             int opti = 0;
2215             while (opti < args.length) {
2216                 String arg = args[opti];
2217                 if ("-h".equals(arg)) {
2218                     dumpHelp(pw);
2219                     return;
2220                 } else if ("-a".equals(arg)) {
2221                     // Ignore, we always dump all.
2222                 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2223                     pw.println("Unknown option: " + arg);
2224                     return;
2225                 } else {
2226                     break;
2227                 }
2228                 opti++;
2229             }
2230             if (opti < args.length) {
2231                 String pkg = args[opti];
2232                 try {
2233                     filterUid = getContext().getPackageManager().getPackageUid(pkg,
2234                             PackageManager.MATCH_ANY_USER);
2235                 } catch (NameNotFoundException ignored) {
2236                     pw.println("Invalid package: " + pkg);
2237                     return;
2238                 }
2239             }
2240         }
2241 
2242         final int filterUidFinal = UserHandle.getAppId(filterUid);
2243         final long nowElapsed = SystemClock.elapsedRealtime();
2244         final long nowUptime = SystemClock.uptimeMillis();
2245         synchronized (mLock) {
2246             mConstants.dump(pw);
2247             pw.println();
2248             pw.println("Started users: " + Arrays.toString(mStartedUsers));
2249             pw.print("Registered ");
2250             pw.print(mJobs.size());
2251             pw.println(" jobs:");
2252             if (mJobs.size() > 0) {
2253                 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2254                 Collections.sort(jobs, new Comparator<JobStatus>() {
2255                     @Override
2256                     public int compare(JobStatus o1, JobStatus o2) {
2257                         int uid1 = o1.getUid();
2258                         int uid2 = o2.getUid();
2259                         int id1 = o1.getJobId();
2260                         int id2 = o2.getJobId();
2261                         if (uid1 != uid2) {
2262                             return uid1 < uid2 ? -1 : 1;
2263                         }
2264                         return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
2265                     }
2266                 });
2267                 for (JobStatus job : jobs) {
2268                     pw.print("  JOB #"); job.printUniqueId(pw); pw.print(": ");
2269                     pw.println(job.toShortStringExceptUniqueId());
2270 
2271                     // Skip printing details if the caller requested a filter
2272                     if (!job.shouldDump(filterUidFinal)) {
2273                         continue;
2274                     }
2275 
2276                     job.dump(pw, "    ", true, nowElapsed);
2277                     pw.print("    Ready: ");
2278                     pw.print(isReadyToBeExecutedLocked(job));
2279                     pw.print(" (job=");
2280                     pw.print(job.isReady());
2281                     pw.print(" user=");
2282                     pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
2283                     pw.print(" !pending=");
2284                     pw.print(!mPendingJobs.contains(job));
2285                     pw.print(" !active=");
2286                     pw.print(!isCurrentlyActiveLocked(job));
2287                     pw.print(" !backingup=");
2288                     pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
2289                     pw.print(" comp=");
2290                     boolean componentPresent = false;
2291                     try {
2292                         componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2293                                 job.getServiceComponent(),
2294                                 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2295                                 job.getUserId()) != null);
2296                     } catch (RemoteException e) {
2297                     }
2298                     pw.print(componentPresent);
2299                     pw.println(")");
2300                 }
2301             } else {
2302                 pw.println("  None.");
2303             }
2304             for (int i=0; i<mControllers.size(); i++) {
2305                 pw.println();
2306                 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
2307             }
2308             pw.println();
2309             pw.println("Uid priority overrides:");
2310             for (int i=0; i< mUidPriorityOverride.size(); i++) {
2311                 int uid = mUidPriorityOverride.keyAt(i);
2312                 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2313                     pw.print("  "); pw.print(UserHandle.formatUid(uid));
2314                     pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2315                 }
2316             }
2317             if (mBackingUpUids.size() > 0) {
2318                 pw.println();
2319                 pw.println("Backing up uids:");
2320                 boolean first = true;
2321                 for (int i = 0; i < mBackingUpUids.size(); i++) {
2322                     int uid = mBackingUpUids.keyAt(i);
2323                     if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2324                         if (first) {
2325                             pw.print("  ");
2326                             first = false;
2327                         } else {
2328                             pw.print(", ");
2329                         }
2330                         pw.print(UserHandle.formatUid(uid));
2331                     }
2332                 }
2333                 pw.println();
2334             }
2335             pw.println();
2336             mJobPackageTracker.dump(pw, "", filterUidFinal);
2337             pw.println();
2338             if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2339                 pw.println();
2340             }
2341             pw.println("Pending queue:");
2342             for (int i=0; i<mPendingJobs.size(); i++) {
2343                 JobStatus job = mPendingJobs.get(i);
2344                 pw.print("  Pending #"); pw.print(i); pw.print(": ");
2345                 pw.println(job.toShortString());
2346                 job.dump(pw, "    ", false, nowElapsed);
2347                 int priority = evaluateJobPriorityLocked(job);
2348                 if (priority != JobInfo.PRIORITY_DEFAULT) {
2349                     pw.print("    Evaluated priority: "); pw.println(priority);
2350                 }
2351                 pw.print("    Tag: "); pw.println(job.getTag());
2352                 pw.print("    Enq: ");
2353                 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
2354                 pw.println();
2355             }
2356             pw.println();
2357             pw.println("Active jobs:");
2358             for (int i=0; i<mActiveServices.size(); i++) {
2359                 JobServiceContext jsc = mActiveServices.get(i);
2360                 pw.print("  Slot #"); pw.print(i); pw.print(": ");
2361                 final JobStatus job = jsc.getRunningJobLocked();
2362                 if (job == null) {
2363                     if (jsc.mStoppedReason != null) {
2364                         pw.print("inactive since ");
2365                         TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
2366                         pw.print(", stopped because: ");
2367                         pw.println(jsc.mStoppedReason);
2368                     } else {
2369                         pw.println("inactive");
2370                     }
2371                     continue;
2372                 } else {
2373                     pw.println(job.toShortString());
2374                     pw.print("    Running for: ");
2375                     TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
2376                     pw.print(", timeout at: ");
2377                     TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
2378                     pw.println();
2379                     job.dump(pw, "    ", false, nowElapsed);
2380                     int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
2381                     if (priority != JobInfo.PRIORITY_DEFAULT) {
2382                         pw.print("    Evaluated priority: "); pw.println(priority);
2383                     }
2384                     pw.print("    Active at ");
2385                     TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
2386                     pw.print(", pending for ");
2387                     TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2388                     pw.println();
2389                 }
2390             }
2391             if (filterUid == -1) {
2392                 pw.println();
2393                 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2394                 pw.print("mReportedActive="); pw.println(mReportedActive);
2395                 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2396             }
2397         }
2398         pw.println();
2399     }
2400 }
2401