blob: 3db2f31f0427bc6959dcbf565ade4d18745e5fb4 [file] [log] [blame]
Christopher Tate7060b042014-06-09 19:50:00 -07001/*
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
17package com.android.server.job;
18
Christopher Tateb5c07882016-05-26 17:11:09 -070019import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
Shreyas Basarge5db09082016-01-07 13:38:29 +000022import java.io.FileDescriptor;
23import java.io.PrintWriter;
24import java.util.ArrayList;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070025import java.util.Arrays;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070026import java.util.Collections;
27import java.util.Comparator;
Shreyas Basarge5db09082016-01-07 13:38:29 +000028import java.util.Iterator;
29import java.util.List;
30
Christopher Tateee7805b2016-07-15 16:56:56 -070031import android.app.Activity;
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070032import android.app.ActivityManager;
Christopher Tate5568f542014-06-18 13:53:31 -070033import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070034import android.app.IUidObserver;
Christopher Tate7060b042014-06-09 19:50:00 -070035import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000036import android.app.job.JobParameters;
Christopher Tate7060b042014-06-09 19:50:00 -070037import android.app.job.JobScheduler;
38import android.app.job.JobService;
Shreyas Basarge5db09082016-01-07 13:38:29 +000039import android.app.job.IJobScheduler;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070040import android.app.job.JobWorkItem;
Christopher Tate7060b042014-06-09 19:50:00 -070041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070043import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070044import android.content.Context;
45import android.content.Intent;
46import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070047import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070048import android.content.pm.PackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070049import android.content.pm.ServiceInfo;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060050import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070051import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070052import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070053import android.os.BatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070054import android.os.Binder;
55import android.os.Handler;
56import android.os.Looper;
57import android.os.Message;
Shreyas Basargecbf5ae92016-03-08 16:13:06 +000058import android.os.Process;
Dianne Hackborn88e98df2015-03-23 13:29:14 -070059import android.os.PowerManager;
Christopher Tate7060b042014-06-09 19:50:00 -070060import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080061import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070062import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070063import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070064import android.os.SystemClock;
65import android.os.UserHandle;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070066import android.provider.Settings;
67import android.util.KeyValueListParser;
Christopher Tate7060b042014-06-09 19:50:00 -070068import android.util.Slog;
69import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080070import android.util.SparseIntArray;
71import android.util.TimeUtils;
Christopher Tate5d346052016-03-08 12:56:08 -080072
Dianne Hackbornfdb19562014-07-11 16:03:36 -070073import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070074import com.android.internal.app.procstats.ProcessStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070075import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060076import com.android.internal.util.DumpUtils;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080077import com.android.server.DeviceIdleController;
78import com.android.server.LocalServices;
Christopher Tate2f36fd62016-02-18 18:36:08 -080079import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080080import com.android.server.job.controllers.AppIdleController;
Christopher Tate7060b042014-06-09 19:50:00 -070081import com.android.server.job.controllers.BatteryController;
82import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080083import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070084import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070085import com.android.server.job.controllers.IdleController;
86import com.android.server.job.controllers.JobStatus;
87import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070088import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -070089import com.android.server.job.controllers.TimeController;
90
Jeff Sharkey822cbd12016-02-25 11:09:55 -070091import libcore.util.EmptyArray;
92
Christopher Tate7060b042014-06-09 19:50:00 -070093/**
94 * Responsible for taking jobs representing work to be performed by a client app, and determining
95 * based on the criteria specified when that job should be run against the client application's
96 * endpoint.
97 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
98 * about constraints, or the state of active jobs. It receives callbacks from the various
99 * controllers and completed jobs and operates accordingly.
100 *
101 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
102 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
103 * @hide
104 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800105public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700106 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800107 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700108 public static final boolean DEBUG = false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800109
Dianne Hackborn970510b2016-02-24 16:56:42 -0800110 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700111 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800112 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800113 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800114 /** The maximum number of jobs that we allow an unprivileged app to schedule */
115 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700116
Christopher Tate2f36fd62016-02-18 18:36:08 -0800117
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800118 /** Global local for all job scheduler state. */
119 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700120 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700121 final JobStore mJobs;
Dianne Hackborn807de782016-04-07 17:54:41 -0700122 /** Tracking amount of time each package runs for. */
123 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700124
125 static final int MSG_JOB_EXPIRED = 0;
126 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700127 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000128 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700129
Christopher Tate7060b042014-06-09 19:50:00 -0700130 /**
131 * Track Services that have currently active or pending jobs. The index is provided by
132 * {@link JobStatus#getServiceToken()}
133 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700134 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700135 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700136 List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800137 /** Need direct access to this for testing. */
138 BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700139 /** Need direct access to this for testing. */
140 StorageController mStorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700141 /**
142 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
143 * when ready to execute them.
144 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700145 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700146
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700147 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700148
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700149 final JobHandler mHandler;
150 final JobSchedulerStub mJobSchedulerStub;
151
152 IBatteryStats mBatteryStats;
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700153 PowerManager mPowerManager;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800154 DeviceIdleController.LocalService mLocalDeviceIdleController;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700155
156 /**
157 * Set to true once we are allowed to run third party apps.
158 */
159 boolean mReadyToRock;
160
Christopher Tate7060b042014-06-09 19:50:00 -0700161 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800162 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800163 */
164 boolean mReportedActive;
165
166 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800167 * Current limit on the number of concurrent JobServiceContext entries we want to
168 * keep actively running a job.
169 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700170 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800171
172 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800173 * Which uids are currently in the foreground.
174 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800175 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
176
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700177 /**
178 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
179 */
180 final SparseIntArray mBackingUpUids = new SparseIntArray();
181
Dianne Hackborn970510b2016-02-24 16:56:42 -0800182 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
183
184 /**
185 * This array essentially stores the state of mActiveServices array.
186 * The ith index stores the job present on the ith JobServiceContext.
187 * We manipulate this array until we arrive at what jobs should be running on
188 * what JobServiceContext.
189 */
190 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
191 /**
192 * Indicates whether we need to act on this jobContext id
193 */
194 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
195 /**
196 * The uid whose jobs we would like to assign to a context.
197 */
198 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800199
200 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700201 * All times are in milliseconds. These constants are kept synchronized with the system
202 * global Settings. Any access to this class or its fields should be done while
203 * holding the JobSchedulerService.mLock lock.
204 */
205 private final class Constants extends ContentObserver {
206 // Key names stored in the settings value.
207 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
208 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800209 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700210 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700211 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
212 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
213 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
214 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
215 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
216 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
217 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
218 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
219 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
220 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
221
222 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
223 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800224 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700225 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700226 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
227 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
228 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
229 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
230 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
231 private static final int DEFAULT_FG_JOB_COUNT = 4;
232 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
233 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700234 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700235 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
236
237 /**
238 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
239 * early.
240 */
241 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
242 /**
243 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
244 * things early.
245 */
246 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
247 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800248 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
249 * schedule things early.
250 */
251 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
252 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700253 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
254 * schedule things early.
255 */
256 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
257 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700258 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
259 * things early. 1 == Run connectivity jobs as soon as ready.
260 */
261 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
262 /**
263 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
264 * schedule things early.
265 */
266 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
267 /**
268 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
269 * running some work early. This (and thus the other min counts) is now set to 1, to
270 * prevent any batching at this level. Since we now do batching through doze, that is
271 * a much better mechanism.
272 */
273 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
274 /**
275 * This is the job execution factor that is considered to be heavy use of the system.
276 */
277 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
278 /**
279 * This is the job execution factor that is considered to be moderate use of the system.
280 */
281 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
282 /**
283 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
284 */
285 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
286 /**
287 * The maximum number of background jobs we allow when the system is in a normal
288 * memory state.
289 */
290 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
291 /**
292 * The maximum number of background jobs we allow when the system is in a moderate
293 * memory state.
294 */
295 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
296 /**
297 * The maximum number of background jobs we allow when the system is in a low
298 * memory state.
299 */
300 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
301 /**
302 * The maximum number of background jobs we allow when the system is in a critical
303 * memory state.
304 */
305 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
306
307 private ContentResolver mResolver;
308 private final KeyValueListParser mParser = new KeyValueListParser(',');
309
310 public Constants(Handler handler) {
311 super(handler);
312 }
313
314 public void start(ContentResolver resolver) {
315 mResolver = resolver;
316 mResolver.registerContentObserver(Settings.Global.getUriFor(
317 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
318 updateConstants();
319 }
320
321 @Override
322 public void onChange(boolean selfChange, Uri uri) {
323 updateConstants();
324 }
325
326 private void updateConstants() {
327 synchronized (mLock) {
328 try {
329 mParser.setString(Settings.Global.getString(mResolver,
330 Settings.Global.ALARM_MANAGER_CONSTANTS));
331 } catch (IllegalArgumentException e) {
332 // Failed to parse the settings string, log this and move on
333 // with defaults.
334 Slog.e(TAG, "Bad device idle settings", e);
335 }
336
337 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
338 DEFAULT_MIN_IDLE_COUNT);
339 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
340 DEFAULT_MIN_CHARGING_COUNT);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800341 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
342 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700343 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
344 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700345 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
346 DEFAULT_MIN_CONNECTIVITY_COUNT);
347 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
348 DEFAULT_MIN_CONTENT_COUNT);
349 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
350 DEFAULT_MIN_READY_JOBS_COUNT);
351 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
352 DEFAULT_HEAVY_USE_FACTOR);
353 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
354 DEFAULT_MODERATE_USE_FACTOR);
355 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
356 DEFAULT_FG_JOB_COUNT);
357 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
358 DEFAULT_BG_NORMAL_JOB_COUNT);
359 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
360 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
361 }
362 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
363 DEFAULT_BG_MODERATE_JOB_COUNT);
364 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
365 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
366 }
367 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
368 DEFAULT_BG_LOW_JOB_COUNT);
369 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
370 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
371 }
372 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
373 DEFAULT_BG_CRITICAL_JOB_COUNT);
374 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
375 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
376 }
377 }
378 }
379
380 void dump(PrintWriter pw) {
381 pw.println(" Settings:");
382
383 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
384 pw.print(MIN_IDLE_COUNT); pw.println();
385
386 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
387 pw.print(MIN_CHARGING_COUNT); pw.println();
388
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800389 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
390 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
391
Dianne Hackborn532ea262017-03-17 17:50:55 -0700392 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
393 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
394
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700395 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
396 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
397
398 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
399 pw.print(MIN_CONTENT_COUNT); pw.println();
400
401 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
402 pw.print(MIN_READY_JOBS_COUNT); pw.println();
403
404 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
405 pw.print(HEAVY_USE_FACTOR); pw.println();
406
407 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
408 pw.print(MODERATE_USE_FACTOR); pw.println();
409
410 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
411 pw.print(FG_JOB_COUNT); pw.println();
412
413 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
414 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
415
416 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
417 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
418
419 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
420 pw.print(BG_LOW_JOB_COUNT); pw.println();
421
422 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
423 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
424 }
425 }
426
427 final Constants mConstants;
428
429 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700430 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
431 * still clean up. On reinstall the package will have a new uid.
432 */
433 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
434 @Override
435 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700436 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700437 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700438 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700439 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700440 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700441 // Purge the app's jobs if the whole package was just disabled. When this is
442 // the case the component name will be a bare package name.
443 final String pkgName = getPackageName(intent);
444 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
445 if (pkgName != null && pkgUid != -1) {
446 final String[] changedComponents = intent.getStringArrayExtra(
447 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
448 if (changedComponents != null) {
449 for (String component : changedComponents) {
450 if (component.equals(pkgName)) {
451 if (DEBUG) {
452 Slog.d(TAG, "Package state change: " + pkgName);
453 }
454 try {
455 final int userId = UserHandle.getUserId(pkgUid);
456 IPackageManager pm = AppGlobals.getPackageManager();
457 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
458 if (state == COMPONENT_ENABLED_STATE_DISABLED
459 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
460 if (DEBUG) {
461 Slog.d(TAG, "Removing jobs for package " + pkgName
462 + " in user " + userId);
463 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800464 cancelJobsForUid(pkgUid);
Christopher Tateb5c07882016-05-26 17:11:09 -0700465 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700466 } catch (RemoteException|IllegalArgumentException e) {
467 /*
468 * IllegalArgumentException means that the package doesn't exist.
469 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
470 * behind outright uninstall, so by the time we try to act it's gone.
471 * We don't need to act on this PACKAGE_CHANGED when this happens;
472 * we'll get a PACKAGE_REMOVED later and clean up then.
473 *
474 * RemoteException can't actually happen; the package manager is
475 * running in this same process.
476 */
477 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700478 break;
479 }
480 }
481 }
482 } else {
483 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
484 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700485 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700486 // If this is an outright uninstall rather than the first half of an
487 // app update sequence, cancel the jobs associated with the app.
488 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
489 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
490 if (DEBUG) {
491 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
492 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800493 cancelJobsForUid(uidRemoved);
Christopher Tate7060b042014-06-09 19:50:00 -0700494 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700495 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700496 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
497 if (DEBUG) {
498 Slog.d(TAG, "Removing jobs for user: " + userId);
499 }
500 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700501 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
502 // Has this package scheduled any jobs, such that we will take action
503 // if it were to be force-stopped?
504 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
505 final String pkgName = intent.getData().getSchemeSpecificPart();
506 if (pkgUid != -1) {
507 List<JobStatus> jobsForUid;
508 synchronized (mLock) {
509 jobsForUid = mJobs.getJobsByUid(pkgUid);
510 }
511 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
512 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
513 if (DEBUG) {
514 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
515 + pkgUid + " has jobs");
516 }
517 setResultCode(Activity.RESULT_OK);
518 break;
519 }
520 }
521 }
522 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
523 // possible force-stop
524 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
525 final String pkgName = intent.getData().getSchemeSpecificPart();
526 if (pkgUid != -1) {
527 if (DEBUG) {
528 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
529 }
530 cancelJobsForPackageAndUid(pkgName, pkgUid);
531 }
Christopher Tate7060b042014-06-09 19:50:00 -0700532 }
533 }
534 };
535
Christopher Tateb5c07882016-05-26 17:11:09 -0700536 private String getPackageName(Intent intent) {
537 Uri uri = intent.getData();
538 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
539 return pkg;
540 }
541
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700542 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Sudheer Shanka80255802017-03-04 14:48:53 -0800543 @Override public void onUidStateChanged(int uid, int procState,
544 long procStateSeq) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800545 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700546 }
547
Dianne Hackborne07641d2016-11-09 15:07:23 -0800548 @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800549 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800550 if (disabled) {
551 cancelJobsForUid(uid);
552 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700553 }
554
555 @Override public void onUidActive(int uid) throws RemoteException {
556 }
557
Dianne Hackborne07641d2016-11-09 15:07:23 -0800558 @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
559 if (disabled) {
560 cancelJobsForUid(uid);
561 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700562 }
563 };
564
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800565 public Object getLock() {
566 return mLock;
567 }
568
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700569 public JobStore getJobStore() {
570 return mJobs;
571 }
572
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700573 @Override
574 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700575 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
576 // Let's kick any outstanding jobs for this user.
577 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
578 }
579
580 @Override
581 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700582 // Let's kick any outstanding jobs for this user.
583 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
584 }
585
586 @Override
587 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700588 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700589 }
590
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700591 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
592 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700593 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800594 if (ActivityManager.getService().isAppStartModeDisabled(uId,
595 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700596 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
597 + " -- package not allowed to start");
598 return JobScheduler.RESULT_FAILURE;
599 }
600 } catch (RemoteException e) {
601 }
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800602 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700603 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
604
605 if (work != null && toCancel != null) {
606 // Fast path: we are adding work to an existing job, and the JobInfo is not
607 // changing. We can just directly enqueue this work in to the job.
608 if (toCancel.getJob().equals(job)) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700609 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700610 return JobScheduler.RESULT_SUCCESS;
611 }
612 }
613
614 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
615 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800616 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800617 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800618 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
619 Slog.w(TAG, "Too many jobs for uid " + uId);
620 throw new IllegalStateException("Apps may not schedule more than "
621 + MAX_JOBS_PER_APP + " distinct jobs");
622 }
623 }
624
Dianne Hackborna47223f2017-03-30 13:49:13 -0700625 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700626 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700627
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700628 if (toCancel != null) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700629 cancelJobImplLocked(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700630 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700631 if (work != null) {
632 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700633 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700634 }
635 startTrackingJobLocked(jobStatus, toCancel);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700636
637 // If the job is immediately ready to run, then we can just immediately
638 // put it in the pending list and try to schedule it. This is especially
639 // important for jobs with a 0 deadline constraint, since they will happen a fair
640 // amount, we want to handle them as quickly as possible, and semantically we want to
641 // make sure we have started holding the wake lock for the job before returning to
642 // the caller.
643 // If the job is not yet ready to run, there is nothing more to do -- we are
644 // now just waiting for one of its controllers to change state and schedule
645 // the job appropriately.
646 if (isReadyToBeExecutedLocked(jobStatus)) {
647 // This is a new job, we can just immediately put it on the pending
648 // list and try to run it.
649 mJobPackageTracker.notePending(jobStatus);
650 mPendingJobs.add(jobStatus);
651 maybeRunPendingJobsLocked();
652 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800653 }
Christopher Tate7060b042014-06-09 19:50:00 -0700654 return JobScheduler.RESULT_SUCCESS;
655 }
656
657 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800658 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800659 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
660 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
661 for (int i = jobs.size() - 1; i >= 0; i--) {
662 JobStatus job = jobs.get(i);
663 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700664 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800665 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700666 }
Christopher Tate7060b042014-06-09 19:50:00 -0700667 }
668
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600669 public JobInfo getPendingJob(int uid, int jobId) {
670 synchronized (mLock) {
671 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
672 for (int i = jobs.size() - 1; i >= 0; i--) {
673 JobStatus job = jobs.get(i);
674 if (job.getJobId() == jobId) {
675 return job.getJob();
676 }
677 }
678 return null;
679 }
680 }
681
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700682 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800683 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700684 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
685 for (int i=0; i<jobsForUser.size(); i++) {
686 JobStatus toRemove = jobsForUser.get(i);
687 cancelJobImplLocked(toRemove, null);
688 }
Christopher Tate7060b042014-06-09 19:50:00 -0700689 }
690 }
691
Christopher Tateee7805b2016-07-15 16:56:56 -0700692 void cancelJobsForPackageAndUid(String pkgName, int uid) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700693 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700694 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
695 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
696 final JobStatus job = jobsForUid.get(i);
697 if (job.getSourcePackageName().equals(pkgName)) {
698 cancelJobImplLocked(job, null);
699 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700700 }
701 }
702 }
703
Christopher Tate7060b042014-06-09 19:50:00 -0700704 /**
705 * Entry point from client to cancel all jobs originating from their uid.
706 * This will remove the job from the master list, and cancel the job if it was staged for
707 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700708 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800709 *
Christopher Tate7060b042014-06-09 19:50:00 -0700710 */
Dianne Hackborne07641d2016-11-09 15:07:23 -0800711 public void cancelJobsForUid(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800712 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700713 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
714 for (int i=0; i<jobsForUid.size(); i++) {
715 JobStatus toRemove = jobsForUid.get(i);
716 cancelJobImplLocked(toRemove, null);
717 }
Christopher Tate7060b042014-06-09 19:50:00 -0700718 }
719 }
720
721 /**
722 * Entry point from client to cancel the job corresponding to the jobId provided.
723 * This will remove the job from the master list, and cancel the job if it was staged for
724 * execution or being executed.
725 * @param uid Uid of the calling client.
726 * @param jobId Id of the job, provided at schedule-time.
727 */
728 public void cancelJob(int uid, int jobId) {
729 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800730 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700731 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700732 if (toCancel != null) {
733 cancelJobImplLocked(toCancel, null);
734 }
Christopher Tate7060b042014-06-09 19:50:00 -0700735 }
736 }
737
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700738 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob) {
739 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
740 cancelled.unprepareLocked(ActivityManager.getService());
741 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
742 // Remove from pending queue.
743 if (mPendingJobs.remove(cancelled)) {
744 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -0700745 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700746 // Cancel if running.
747 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
748 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700749 }
750
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800751 void updateUidState(int uid, int procState) {
752 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800753 if (procState == ActivityManager.PROCESS_STATE_TOP) {
754 // Only use this if we are exactly the top app. All others can live
755 // with just the foreground priority. This means that persistent processes
756 // can never be the top app priority... that is fine.
757 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
758 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
759 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800760 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800761 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800762 }
763 }
764 }
765
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700766 @Override
767 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800768 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700769 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -0600770 // When becoming idle, make sure no jobs are actively running,
771 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700772 for (int i=0; i<mActiveServices.size(); i++) {
773 JobServiceContext jsc = mActiveServices.get(i);
774 final JobStatus executing = jsc.getRunningJob();
Jeff Sharkey34618b52016-06-01 15:51:19 -0600775 if (executing != null
776 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700777 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700778 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700779 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700780 } else {
781 // When coming out of idle, allow thing to start back up.
782 if (mReadyToRock) {
783 if (mLocalDeviceIdleController != null) {
784 if (!mReportedActive) {
785 mReportedActive = true;
786 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700787 }
788 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700789 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700790 }
791 }
792 }
793 }
794
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700795 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000796 // active is true if pending queue contains jobs OR some job is running.
797 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800798 if (mPendingJobs.size() <= 0) {
799 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700800 final JobServiceContext jsc = mActiveServices.get(i);
801 final JobStatus job = jsc.getRunningJob();
802 if (job != null
803 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
804 && !job.dozeWhitelisted) {
805 // We will report active if we have a job running and it is not an exception
806 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800807 active = true;
808 break;
809 }
810 }
811 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000812
813 if (mReportedActive != active) {
814 mReportedActive = active;
815 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800816 mLocalDeviceIdleController.setJobsActive(active);
817 }
818 }
819 }
820
Christopher Tate7060b042014-06-09 19:50:00 -0700821 /**
822 * Initializes the system service.
823 * <p>
824 * Subclasses must define a single argument constructor that accepts the context
825 * and passes it to super.
826 * </p>
827 *
828 * @param context The system server context.
829 */
830 public JobSchedulerService(Context context) {
831 super(context);
Dianne Hackborn970e3f42016-06-01 10:55:13 -0700832 mHandler = new JobHandler(context.getMainLooper());
833 mConstants = new Constants(mHandler);
834 mJobSchedulerStub = new JobSchedulerStub();
835 mJobs = JobStore.initAndGet(this);
836
Christopher Tate7060b042014-06-09 19:50:00 -0700837 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700838 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700839 mControllers.add(ConnectivityController.get(this));
840 mControllers.add(TimeController.get(this));
841 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800842 mBatteryController = BatteryController.get(this);
843 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700844 mStorageController = StorageController.get(this);
845 mControllers.add(mStorageController);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800846 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800847 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700848 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700849 }
850
851 @Override
852 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000853 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700854 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
855 }
856
857 @Override
858 public void onBootPhase(int phase) {
859 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700860 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000861 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700862 final IntentFilter filter = new IntentFilter();
863 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
864 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -0700865 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
866 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -0700867 filter.addDataScheme("package");
868 getContext().registerReceiverAsUser(
869 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
870 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
871 getContext().registerReceiverAsUser(
872 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000873 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700874 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800875 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800876 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800877 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
878 null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700879 } catch (RemoteException e) {
880 // ignored; both services live in system_server
881 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700882 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800883 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700884 // Let's go!
885 mReadyToRock = true;
886 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
887 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800888 mLocalDeviceIdleController
889 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700890 // Create the "runners".
891 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
892 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700893 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700894 getContext().getMainLooper()));
895 }
896 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800897 mJobs.forEachJob(new JobStatusFunctor() {
898 @Override
899 public void process(JobStatus job) {
900 for (int controller = 0; controller < mControllers.size(); controller++) {
901 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800902 sc.maybeStartTrackingJobLocked(job, null);
903 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700904 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800905 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700906 // GO GO GO!
907 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
908 }
Christopher Tate7060b042014-06-09 19:50:00 -0700909 }
910 }
911
912 /**
913 * Called when we have a job status object that we need to insert in our
914 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
915 * about.
916 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700917 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
918 if (!jobStatus.isPreparedLocked()) {
919 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
920 }
921 final boolean update = mJobs.add(jobStatus);
922 if (mReadyToRock) {
923 for (int i = 0; i < mControllers.size(); i++) {
924 StateController controller = mControllers.get(i);
925 if (update) {
926 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700927 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700928 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -0700929 }
Christopher Tate7060b042014-06-09 19:50:00 -0700930 }
931 }
932
933 /**
934 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
935 * object removed.
936 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700937 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700938 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700939 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700940 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700941
942 // Remove from store as well as controllers.
943 final boolean removed = mJobs.remove(jobStatus, writeBack);
944 if (removed && mReadyToRock) {
945 for (int i=0; i<mControllers.size(); i++) {
946 StateController controller = mControllers.get(i);
947 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -0700948 }
949 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700950 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -0700951 }
952
Shreyas Basarge5db09082016-01-07 13:38:29 +0000953 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700954 for (int i=0; i<mActiveServices.size(); i++) {
955 JobServiceContext jsc = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700956 final JobStatus executing = jsc.getRunningJob();
957 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700958 jsc.cancelExecutingJobLocked(reason);
Christopher Tate7060b042014-06-09 19:50:00 -0700959 return true;
960 }
961 }
962 return false;
963 }
964
965 /**
966 * @param job JobStatus we are querying against.
967 * @return Whether or not the job represented by the status object is currently being run or
968 * is pending.
969 */
970 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700971 for (int i=0; i<mActiveServices.size(); i++) {
972 JobServiceContext serviceContext = mActiveServices.get(i);
Christopher Tateeafb5352016-10-04 16:34:48 -0700973 // The 'unsafe' direct-internal-reference running-job inspector is okay to
974 // use here because we are already holding the necessary lock *and* we
975 // immediately discard the returned object reference, if any; we return
976 // only a boolean state indicator to the caller.
977 final JobStatus running = serviceContext.getRunningJobUnsafeLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700978 if (running != null && running.matches(job.getUid(), job.getJobId())) {
979 return true;
980 }
981 }
982 return false;
983 }
984
Dianne Hackborn807de782016-04-07 17:54:41 -0700985 void noteJobsPending(List<JobStatus> jobs) {
986 for (int i = jobs.size() - 1; i >= 0; i--) {
987 JobStatus job = jobs.get(i);
988 mJobPackageTracker.notePending(job);
989 }
990 }
991
992 void noteJobsNonpending(List<JobStatus> jobs) {
993 for (int i = jobs.size() - 1; i >= 0; i--) {
994 JobStatus job = jobs.get(i);
995 mJobPackageTracker.noteNonpending(job);
996 }
997 }
998
Christopher Tate7060b042014-06-09 19:50:00 -0700999 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001000 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1001 * specify an override deadline on a failed job (the failed job will run even though it's not
1002 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1003 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1004 *
Christopher Tate7060b042014-06-09 19:50:00 -07001005 * @param failureToReschedule Provided job status that we will reschedule.
1006 * @return A newly instantiated JobStatus with the same constraints as the last job except
1007 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001008 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001009 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001010 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001011 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001012 final long elapsedNowMillis = SystemClock.elapsedRealtime();
1013 final JobInfo job = failureToReschedule.getJob();
1014
1015 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001016 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1017 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001018
1019 switch (job.getBackoffPolicy()) {
Matthew Williamsd1c06752014-08-22 14:15:28 -07001020 case JobInfo.BACKOFF_POLICY_LINEAR:
1021 delayMillis = initialBackoffMillis * backoffAttempts;
Christopher Tate7060b042014-06-09 19:50:00 -07001022 break;
1023 default:
1024 if (DEBUG) {
1025 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1026 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001027 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
1028 delayMillis =
1029 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
Christopher Tate7060b042014-06-09 19:50:00 -07001030 break;
1031 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001032 delayMillis =
1033 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001034 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -07001035 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001036 for (int ic=0; ic<mControllers.size(); ic++) {
1037 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001038 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001039 }
1040 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001041 }
1042
1043 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001044 * Called after a periodic has executed so we can reschedule it. We take the last execution
1045 * time of the job to be the time of completion (i.e. the time at which this function is
1046 * called).
Christopher Tate7060b042014-06-09 19:50:00 -07001047 * This could be inaccurate b/c the job can run for as long as
1048 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1049 * to underscheduling at least, rather than if we had taken the last execution time to be the
1050 * start of the execution.
1051 * @return A new job representing the execution criteria for this instantiation of the
1052 * recurring job.
1053 */
1054 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1055 final long elapsedNow = SystemClock.elapsedRealtime();
1056 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001057 long runEarly = 0L;
1058
1059 // If this periodic was rescheduled it won't have a deadline.
1060 if (periodicToReschedule.hasDeadlineConstraint()) {
1061 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1062 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001063 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001064 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001065 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1066 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001067
1068 if (DEBUG) {
1069 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1070 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1071 }
1072 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1073 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1074 }
1075
1076 // JobCompletedListener implementations.
1077
1078 /**
1079 * A job just finished executing. We fetch the
1080 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1081 * whether we want to reschedule we readd it to the controllers.
1082 * @param jobStatus Completed job.
1083 * @param needsReschedule Whether the implementing class should reschedule this job.
1084 */
1085 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001086 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001087 if (DEBUG) {
1088 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1089 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001090
1091 // If the job wants to be rescheduled, we first need to make the next upcoming
1092 // job so we can transfer any appropriate state over from the previous job when
1093 // we stop it.
1094 final JobStatus rescheduledJob = needsReschedule
1095 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1096
Shreyas Basarge73f10252016-02-11 17:06:13 +00001097 // Do not write back immediately if this is a periodic job. The job may get lost if system
1098 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001099 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001100 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001101 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001102 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001103 // We still want to check for jobs to execute, because this job may have
1104 // scheduled a new job under the same job id, and now we can run it.
1105 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001106 return;
1107 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001108
1109 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001110 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001111 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001112 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001113 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001114 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001115 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001116 } else if (jobStatus.getJob().isPeriodic()) {
1117 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001118 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001119 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001120 } catch (SecurityException e) {
1121 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1122 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001123 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001124 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001125 jobStatus.unprepareLocked(ActivityManager.getService());
1126 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001127 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001128 }
1129
1130 // StateChangedListener implementations.
1131
1132 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001133 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1134 * some controller's state has changed, so as to run through the list of jobs and start/stop
1135 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001136 */
1137 @Override
1138 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001139 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001140 }
1141
1142 @Override
1143 public void onRunJobNow(JobStatus jobStatus) {
1144 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1145 }
1146
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001147 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001148
1149 public JobHandler(Looper looper) {
1150 super(looper);
1151 }
1152
1153 @Override
1154 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001155 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001156 if (!mReadyToRock) {
1157 return;
1158 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001159 switch (message.what) {
1160 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001161 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001162 // runNow can be null, which is a controller's way of indicating that its
1163 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001164 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001165 mJobPackageTracker.notePending(runNow);
Christopher Tate7060b042014-06-09 19:50:00 -07001166 mPendingJobs.add(runNow);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001167 } else {
1168 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001169 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001170 } break;
1171 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001172 if (mReportedActive) {
1173 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001174 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001175 } else {
1176 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001177 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001178 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001179 break;
1180 case MSG_CHECK_JOB_GREEDY:
1181 queueReadyJobsForExecutionLocked();
1182 break;
1183 case MSG_STOP_JOB:
1184 cancelJobImplLocked((JobStatus) message.obj, null);
1185 break;
Matthew Williams75fc5252014-09-02 16:17:53 -07001186 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001187 maybeRunPendingJobsLocked();
1188 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1189 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001190 }
1191 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001192 }
Christopher Tate7060b042014-06-09 19:50:00 -07001193
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001194 /**
1195 * Run through list of jobs and execute all possible - at least one is expired so we do
1196 * as many as we can.
1197 */
1198 private void queueReadyJobsForExecutionLocked() {
1199 if (DEBUG) {
1200 Slog.d(TAG, "queuing all ready jobs for execution:");
1201 }
1202 noteJobsNonpending(mPendingJobs);
1203 mPendingJobs.clear();
1204 mJobs.forEachJob(mReadyQueueFunctor);
1205 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001206
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001207 if (DEBUG) {
1208 final int queuedJobs = mPendingJobs.size();
1209 if (queuedJobs == 0) {
1210 Slog.d(TAG, "No jobs pending.");
1211 } else {
1212 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001213 }
1214 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001215 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001216
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001217 final class ReadyJobQueueFunctor implements JobStatusFunctor {
1218 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001219
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001220 @Override
1221 public void process(JobStatus job) {
1222 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001223 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001224 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001225 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001226 if (newReadyJobs == null) {
1227 newReadyJobs = new ArrayList<JobStatus>();
1228 }
1229 newReadyJobs.add(job);
1230 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1231 stopJobOnServiceContextLocked(job,
1232 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001233 }
1234 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001235
1236 public void postProcess() {
1237 if (newReadyJobs != null) {
1238 noteJobsPending(newReadyJobs);
1239 mPendingJobs.addAll(newReadyJobs);
1240 }
1241 newReadyJobs = null;
1242 }
1243 }
1244 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1245
1246 /**
1247 * The state of at least one job has changed. Here is where we could enforce various
1248 * policies on when we want to execute jobs.
1249 * Right now the policy is such:
1250 * If >1 of the ready jobs is idle mode we send all of them off
1251 * if more than 2 network connectivity jobs are ready we send them all off.
1252 * If more than 4 jobs total are ready we send them all off.
1253 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1254 */
1255 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1256 int chargingCount;
1257 int batteryNotLowCount;
1258 int storageNotLowCount;
1259 int idleCount;
1260 int backoffCount;
1261 int connectivityCount;
1262 int contentCount;
1263 List<JobStatus> runnableJobs;
1264
1265 public MaybeReadyJobQueueFunctor() {
1266 reset();
1267 }
1268
1269 // Functor method invoked for each job via JobStore.forEachJob()
1270 @Override
1271 public void process(JobStatus job) {
1272 if (isReadyToBeExecutedLocked(job)) {
1273 try {
1274 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1275 job.getJob().getService().getPackageName())) {
1276 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1277 + job.getJob().toString() + " -- package not allowed to start");
1278 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1279 return;
1280 }
1281 } catch (RemoteException e) {
1282 }
1283 if (job.getNumFailures() > 0) {
1284 backoffCount++;
1285 }
1286 if (job.hasIdleConstraint()) {
1287 idleCount++;
1288 }
1289 if (job.hasConnectivityConstraint()) {
1290 connectivityCount++;
1291 }
1292 if (job.hasChargingConstraint()) {
1293 chargingCount++;
1294 }
1295 if (job.hasBatteryNotLowConstraint()) {
1296 batteryNotLowCount++;
1297 }
1298 if (job.hasStorageNotLowConstraint()) {
1299 storageNotLowCount++;
1300 }
1301 if (job.hasContentTriggerConstraint()) {
1302 contentCount++;
1303 }
1304 if (runnableJobs == null) {
1305 runnableJobs = new ArrayList<>();
1306 }
1307 runnableJobs.add(job);
1308 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1309 stopJobOnServiceContextLocked(job,
1310 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1311 }
1312 }
1313
1314 public void postProcess() {
1315 if (backoffCount > 0 ||
1316 idleCount >= mConstants.MIN_IDLE_COUNT ||
1317 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1318 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1319 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1320 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1321 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1322 (runnableJobs != null
1323 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1324 if (DEBUG) {
1325 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1326 }
1327 noteJobsPending(runnableJobs);
1328 mPendingJobs.addAll(runnableJobs);
1329 } else {
1330 if (DEBUG) {
1331 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1332 }
1333 }
1334
1335 // Be ready for next time
1336 reset();
1337 }
1338
1339 private void reset() {
1340 chargingCount = 0;
1341 idleCount = 0;
1342 backoffCount = 0;
1343 connectivityCount = 0;
1344 batteryNotLowCount = 0;
1345 storageNotLowCount = 0;
1346 contentCount = 0;
1347 runnableJobs = null;
1348 }
1349 }
1350 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1351
1352 private void maybeQueueReadyJobsForExecutionLocked() {
1353 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1354
1355 noteJobsNonpending(mPendingJobs);
1356 mPendingJobs.clear();
1357 mJobs.forEachJob(mMaybeQueueFunctor);
1358 mMaybeQueueFunctor.postProcess();
1359 }
1360
1361 /**
1362 * Criteria for moving a job into the pending queue:
1363 * - It's ready.
1364 * - It's not pending.
1365 * - It's not already running on a JSC.
1366 * - The user that requested the job is running.
1367 * - The component is enabled and runnable.
1368 */
1369 private boolean isReadyToBeExecutedLocked(JobStatus job) {
1370 final boolean jobExists = mJobs.containsJob(job);
1371 final boolean jobReady = job.isReady();
1372 final boolean jobPending = mPendingJobs.contains(job);
1373 final boolean jobActive = isCurrentlyActiveLocked(job);
1374 final boolean jobBackingUp = mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0;
1375
1376 final int userId = job.getUserId();
1377 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1378
1379 if (DEBUG) {
1380 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1381 + " exists=" + jobExists
1382 + " ready=" + jobReady + " pending=" + jobPending
1383 + " active=" + jobActive + " backingup=" + jobBackingUp
1384 + " userStarted=" + userStarted);
1385 }
1386
1387 // Short circuit: don't do the expensive PM check unless we really think
1388 // we might need to run this job now.
1389 if (!jobExists || !userStarted || !jobReady || jobPending || jobActive || jobBackingUp) {
1390 return false;
1391 }
1392
1393 final boolean componentPresent;
1394 try {
1395 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1396 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1397 userId) != null);
1398 } catch (RemoteException e) {
1399 throw e.rethrowAsRuntimeException();
1400 }
1401
1402 if (DEBUG) {
1403 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1404 + " componentPresent=" + componentPresent);
1405 }
1406
1407 // Everything else checked out so far, so this is the final yes/no check
1408 return componentPresent;
1409 }
1410
1411 /**
1412 * Criteria for cancelling an active job:
1413 * - It's not ready
1414 * - It's running on a JSC.
1415 */
1416 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
1417 return !job.isReady() && isCurrentlyActiveLocked(job);
1418 }
1419
1420 /**
1421 * Reconcile jobs in the pending queue against available execution contexts.
1422 * A controller can force a job into the pending queue even if it's already running, but
1423 * here is where we decide whether to actually execute it.
1424 */
1425 private void maybeRunPendingJobsLocked() {
1426 if (DEBUG) {
1427 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1428 }
1429 assignJobsToContextsLocked();
1430 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001431 }
1432
Dianne Hackborn807de782016-04-07 17:54:41 -07001433 private int adjustJobPriority(int curPriority, JobStatus job) {
1434 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1435 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001436 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001437 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001438 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001439 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1440 }
1441 }
1442 return curPriority;
1443 }
1444
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001445 private int evaluateJobPriorityLocked(JobStatus job) {
1446 int priority = job.getPriority();
1447 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001448 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001449 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001450 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1451 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001452 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001453 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001454 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001455 }
1456
Christopher Tate7060b042014-06-09 19:50:00 -07001457 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001458 * Takes jobs from pending queue and runs them on available contexts.
1459 * If no contexts are available, preempts lower priority jobs to
1460 * run higher priority ones.
1461 * Lock on mJobs before calling this function.
1462 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001463 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001464 if (DEBUG) {
1465 Slog.d(TAG, printPendingQueue());
1466 }
1467
Dianne Hackborn970510b2016-02-24 16:56:42 -08001468 int memLevel;
1469 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001470 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001471 } catch (RemoteException e) {
1472 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1473 }
1474 switch (memLevel) {
1475 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001476 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001477 break;
1478 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001479 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001480 break;
1481 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001482 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001483 break;
1484 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001485 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001486 break;
1487 }
1488
1489 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1490 boolean[] act = mTmpAssignAct;
1491 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1492 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001493 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001494 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1495 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001496 final JobStatus status = js.getRunningJob();
1497 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001498 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001499 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1500 numForeground++;
1501 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001502 }
1503 act[i] = false;
1504 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001505 }
1506 if (DEBUG) {
1507 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1508 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001509 for (int i=0; i<mPendingJobs.size(); i++) {
1510 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001511
1512 // If job is already running, go to next job.
1513 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1514 if (jobRunningContext != -1) {
1515 continue;
1516 }
1517
Dianne Hackborn970510b2016-02-24 16:56:42 -08001518 final int priority = evaluateJobPriorityLocked(nextPending);
1519 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001520
Shreyas Basarge5db09082016-01-07 13:38:29 +00001521 // Find a context for nextPending. The context should be available OR
1522 // it should have lowest priority among all running jobs
1523 // (sharing the same Uid as nextPending)
1524 int minPriority = Integer.MAX_VALUE;
1525 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001526 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1527 JobStatus job = contextIdToJobMap[j];
1528 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001529 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001530 if ((numActive < mMaxActiveJobs ||
1531 (priority >= JobInfo.PRIORITY_TOP_APP &&
1532 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001533 (preferredUid == nextPending.getUid() ||
1534 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1535 // This slot is free, and we haven't yet hit the limit on
1536 // concurrent jobs... we can just throw the job in to here.
1537 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001538 break;
1539 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001540 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001541 // the context has a preferred Uid or we have reached the limit on
1542 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001543 continue;
1544 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001545 if (job.getUid() != nextPending.getUid()) {
1546 continue;
1547 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001548 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001549 continue;
1550 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001551 if (minPriority > nextPending.lastEvaluatedPriority) {
1552 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001553 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001554 }
1555 }
1556 if (minPriorityContextId != -1) {
1557 contextIdToJobMap[minPriorityContextId] = nextPending;
1558 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001559 numActive++;
1560 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1561 numForeground++;
1562 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001563 }
1564 }
1565 if (DEBUG) {
1566 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1567 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001568 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001569 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001570 boolean preservePreferredUid = false;
1571 if (act[i]) {
1572 JobStatus js = mActiveServices.get(i).getRunningJob();
1573 if (js != null) {
1574 if (DEBUG) {
1575 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1576 }
1577 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001578 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001579 preservePreferredUid = true;
1580 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001581 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001582 if (DEBUG) {
1583 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001584 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001585 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001586 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001587 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001588 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001589 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1590 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001591 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001592 if (mPendingJobs.remove(pendingJob)) {
1593 mJobPackageTracker.noteNonpending(pendingJob);
1594 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001595 }
1596 }
1597 if (!preservePreferredUid) {
1598 mActiveServices.get(i).clearPreferredUid();
1599 }
1600 }
1601 }
1602
1603 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1604 for (int i=0; i<map.length; i++) {
1605 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1606 return i;
1607 }
1608 }
1609 return -1;
1610 }
1611
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001612 final class LocalService implements JobSchedulerInternal {
1613
1614 /**
1615 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1616 * jobs are always considered pending.
1617 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001618 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001619 public List<JobInfo> getSystemScheduledPendingJobs() {
1620 synchronized (mLock) {
1621 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1622 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1623 @Override
1624 public void process(JobStatus job) {
1625 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1626 pendingJobs.add(job.getJob());
1627 }
1628 }
1629 });
1630 return pendingJobs;
1631 }
1632 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001633
1634 @Override
1635 public void addBackingUpUid(int uid) {
1636 synchronized (mLock) {
1637 // No need to actually do anything here, since for a full backup the
1638 // activity manager will kill the process which will kill the job (and
1639 // cause it to restart, but now it can't run).
1640 mBackingUpUids.put(uid, uid);
1641 }
1642 }
1643
1644 @Override
1645 public void removeBackingUpUid(int uid) {
1646 synchronized (mLock) {
1647 mBackingUpUids.delete(uid);
1648 // If there are any jobs for this uid, we need to rebuild the pending list
1649 // in case they are now ready to run.
1650 if (mJobs.countJobsForUid(uid) > 0) {
1651 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1652 }
1653 }
1654 }
1655
1656 @Override
1657 public void clearAllBackingUpUids() {
1658 synchronized (mLock) {
1659 if (mBackingUpUids.size() > 0) {
1660 mBackingUpUids.clear();
1661 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1662 }
1663 }
1664 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001665 }
1666
Shreyas Basarge5db09082016-01-07 13:38:29 +00001667 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001668 * Binder stub trampoline implementation
1669 */
1670 final class JobSchedulerStub extends IJobScheduler.Stub {
1671 /** Cache determination of whether a given app can persist jobs
1672 * key is uid of the calling app; value is undetermined/true/false
1673 */
1674 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1675
1676 // Enforce that only the app itself (or shared uid participant) can schedule a
1677 // job that runs one of the app's services, as well as verifying that the
1678 // named service properly requires the BIND_JOB_SERVICE permission
1679 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001680 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001681 final ComponentName service = job.getService();
1682 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001683 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001684 PackageManager.MATCH_DIRECT_BOOT_AWARE
1685 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001686 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001687 if (si == null) {
1688 throw new IllegalArgumentException("No such service " + service);
1689 }
Christopher Tate7060b042014-06-09 19:50:00 -07001690 if (si.applicationInfo.uid != uid) {
1691 throw new IllegalArgumentException("uid " + uid +
1692 " cannot schedule job in " + service.getPackageName());
1693 }
1694 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1695 throw new IllegalArgumentException("Scheduled service " + service
1696 + " does not require android.permission.BIND_JOB_SERVICE permission");
1697 }
Christopher Tate5568f542014-06-18 13:53:31 -07001698 } catch (RemoteException e) {
1699 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001700 }
1701 }
1702
1703 private boolean canPersistJobs(int pid, int uid) {
1704 // If we get this far we're good to go; all we need to do now is check
1705 // whether the app is allowed to persist its scheduled work.
1706 final boolean canPersist;
1707 synchronized (mPersistCache) {
1708 Boolean cached = mPersistCache.get(uid);
1709 if (cached != null) {
1710 canPersist = cached.booleanValue();
1711 } else {
1712 // Persisting jobs is tantamount to running at boot, so we permit
1713 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1714 // permission
1715 int result = getContext().checkPermission(
1716 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1717 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1718 mPersistCache.put(uid, canPersist);
1719 }
1720 }
1721 return canPersist;
1722 }
1723
1724 // IJobScheduler implementation
1725 @Override
1726 public int schedule(JobInfo job) throws RemoteException {
1727 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001728 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001729 }
1730 final int pid = Binder.getCallingPid();
1731 final int uid = Binder.getCallingUid();
1732
1733 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001734 if (job.isPersisted()) {
1735 if (!canPersistJobs(pid, uid)) {
1736 throw new IllegalArgumentException("Error: requested job be persisted without"
1737 + " holding RECEIVE_BOOT_COMPLETED permission.");
1738 }
1739 }
Christopher Tate7060b042014-06-09 19:50:00 -07001740
Jeff Sharkey785f4942016-07-14 10:31:15 -06001741 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1742 getContext().enforceCallingOrSelfPermission(
1743 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1744 }
1745
Christopher Tate7060b042014-06-09 19:50:00 -07001746 long ident = Binder.clearCallingIdentity();
1747 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001748 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1749 } finally {
1750 Binder.restoreCallingIdentity(ident);
1751 }
1752 }
1753
1754 // IJobScheduler implementation
1755 @Override
1756 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1757 if (DEBUG) {
1758 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1759 }
1760 final int pid = Binder.getCallingPid();
1761 final int uid = Binder.getCallingUid();
1762
1763 enforceValidJobRequest(uid, job);
1764 if (job.isPersisted()) {
1765 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1766 }
1767 if (work == null) {
1768 throw new NullPointerException("work is null");
1769 }
1770
1771 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1772 getContext().enforceCallingOrSelfPermission(
1773 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1774 }
1775
1776 long ident = Binder.clearCallingIdentity();
1777 try {
1778 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
Christopher Tate7060b042014-06-09 19:50:00 -07001779 } finally {
1780 Binder.restoreCallingIdentity(ident);
1781 }
1782 }
1783
1784 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001785 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001786 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001787 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001788 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001789 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1790 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001791 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001792
1793 if (packageName == null) {
1794 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001795 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001796
1797 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1798 android.Manifest.permission.UPDATE_DEVICE_STATS);
1799 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1800 throw new SecurityException("Caller uid " + callerUid
1801 + " not permitted to schedule jobs for other apps");
1802 }
1803
Jeff Sharkey4f100402016-05-03 17:44:23 -06001804 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1805 getContext().enforceCallingOrSelfPermission(
1806 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1807 }
1808
Shreyas Basarge968ac752016-01-11 23:09:26 +00001809 long ident = Binder.clearCallingIdentity();
1810 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001811 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001812 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001813 } finally {
1814 Binder.restoreCallingIdentity(ident);
1815 }
1816 }
1817
1818 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001819 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1820 final int uid = Binder.getCallingUid();
1821
1822 long ident = Binder.clearCallingIdentity();
1823 try {
1824 return JobSchedulerService.this.getPendingJobs(uid);
1825 } finally {
1826 Binder.restoreCallingIdentity(ident);
1827 }
1828 }
1829
1830 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001831 public JobInfo getPendingJob(int jobId) throws RemoteException {
1832 final int uid = Binder.getCallingUid();
1833
1834 long ident = Binder.clearCallingIdentity();
1835 try {
1836 return JobSchedulerService.this.getPendingJob(uid, jobId);
1837 } finally {
1838 Binder.restoreCallingIdentity(ident);
1839 }
1840 }
1841
1842 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001843 public void cancelAll() throws RemoteException {
1844 final int uid = Binder.getCallingUid();
1845
1846 long ident = Binder.clearCallingIdentity();
1847 try {
Dianne Hackborne07641d2016-11-09 15:07:23 -08001848 JobSchedulerService.this.cancelJobsForUid(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001849 } finally {
1850 Binder.restoreCallingIdentity(ident);
1851 }
1852 }
1853
1854 @Override
1855 public void cancel(int jobId) throws RemoteException {
1856 final int uid = Binder.getCallingUid();
1857
1858 long ident = Binder.clearCallingIdentity();
1859 try {
1860 JobSchedulerService.this.cancelJob(uid, jobId);
1861 } finally {
1862 Binder.restoreCallingIdentity(ident);
1863 }
1864 }
1865
1866 /**
1867 * "dumpsys" infrastructure
1868 */
1869 @Override
1870 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06001871 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07001872
1873 long identityToken = Binder.clearCallingIdentity();
1874 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001875 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001876 } finally {
1877 Binder.restoreCallingIdentity(identityToken);
1878 }
1879 }
Christopher Tate5d346052016-03-08 12:56:08 -08001880
1881 @Override
1882 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001883 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08001884 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07001885 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08001886 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001887 };
1888
Christopher Tate5d346052016-03-08 12:56:08 -08001889 // Shell command infrastructure: run the given job immediately
1890 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1891 if (DEBUG) {
1892 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1893 + " " + jobId + " f=" + force);
1894 }
1895
1896 try {
1897 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1898 if (uid < 0) {
1899 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1900 }
1901
1902 synchronized (mLock) {
1903 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1904 if (js == null) {
1905 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1906 }
1907
1908 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1909 if (!js.isConstraintsSatisfied()) {
1910 js.overrideState = 0;
1911 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1912 }
1913
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001914 queueReadyJobsForExecutionLocked();
1915 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08001916 }
1917 } catch (RemoteException e) {
1918 // can't happen
1919 }
1920 return 0;
1921 }
1922
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001923 void setMonitorBattery(boolean enabled) {
1924 synchronized (mLock) {
1925 if (mBatteryController != null) {
1926 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
1927 }
1928 }
1929 }
1930
1931 int getBatterySeq() {
1932 synchronized (mLock) {
1933 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
1934 }
1935 }
1936
1937 boolean getBatteryCharging() {
1938 synchronized (mLock) {
1939 return mBatteryController != null
1940 ? mBatteryController.getTracker().isOnStablePower() : false;
1941 }
1942 }
1943
1944 boolean getBatteryNotLow() {
1945 synchronized (mLock) {
1946 return mBatteryController != null
1947 ? mBatteryController.getTracker().isBatteryNotLow() : false;
1948 }
1949 }
1950
Dianne Hackborn532ea262017-03-17 17:50:55 -07001951 int getStorageSeq() {
1952 synchronized (mLock) {
1953 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
1954 }
1955 }
1956
1957 boolean getStorageNotLow() {
1958 synchronized (mLock) {
1959 return mStorageController != null
1960 ? mStorageController.getTracker().isStorageNotLow() : false;
1961 }
1962 }
1963
Shreyas Basarge5db09082016-01-07 13:38:29 +00001964 private String printContextIdToJobMap(JobStatus[] map, String initial) {
1965 StringBuilder s = new StringBuilder(initial + ": ");
1966 for (int i=0; i<map.length; i++) {
1967 s.append("(")
1968 .append(map[i] == null? -1: map[i].getJobId())
1969 .append(map[i] == null? -1: map[i].getUid())
1970 .append(")" );
1971 }
1972 return s.toString();
1973 }
1974
1975 private String printPendingQueue() {
1976 StringBuilder s = new StringBuilder("Pending queue: ");
1977 Iterator<JobStatus> it = mPendingJobs.iterator();
1978 while (it.hasNext()) {
1979 JobStatus js = it.next();
1980 s.append("(")
1981 .append(js.getJob().getId())
1982 .append(", ")
1983 .append(js.getUid())
1984 .append(") ");
1985 }
1986 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07001987 }
Christopher Tate7060b042014-06-09 19:50:00 -07001988
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001989 static void dumpHelp(PrintWriter pw) {
1990 pw.println("Job Scheduler (jobscheduler) dump options:");
1991 pw.println(" [-h] [package] ...");
1992 pw.println(" -h: print this help");
1993 pw.println(" [package] is an optional package name to limit the output to.");
1994 }
1995
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001996 void dumpInternal(final PrintWriter pw, String[] args) {
1997 int filterUid = -1;
1998 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001999 int opti = 0;
2000 while (opti < args.length) {
2001 String arg = args[opti];
2002 if ("-h".equals(arg)) {
2003 dumpHelp(pw);
2004 return;
2005 } else if ("-a".equals(arg)) {
2006 // Ignore, we always dump all.
2007 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2008 pw.println("Unknown option: " + arg);
2009 return;
2010 } else {
2011 break;
2012 }
2013 opti++;
2014 }
2015 if (opti < args.length) {
2016 String pkg = args[opti];
2017 try {
2018 filterUid = getContext().getPackageManager().getPackageUid(pkg,
Amith Yamasani0d1fd8d2016-10-12 14:21:51 -07002019 PackageManager.MATCH_ANY_USER);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002020 } catch (NameNotFoundException ignored) {
2021 pw.println("Invalid package: " + pkg);
2022 return;
2023 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002024 }
2025 }
2026
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002027 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07002028 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002029 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002030 mConstants.dump(pw);
2031 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07002032 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002033 pw.print("Registered ");
2034 pw.print(mJobs.size());
2035 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07002036 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002037 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2038 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002039 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002040 public int compare(JobStatus o1, JobStatus o2) {
2041 int uid1 = o1.getUid();
2042 int uid2 = o2.getUid();
2043 int id1 = o1.getJobId();
2044 int id2 = o2.getJobId();
2045 if (uid1 != uid2) {
2046 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002047 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002048 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08002049 }
2050 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002051 for (JobStatus job : jobs) {
2052 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2053 pw.println(job.toShortStringExceptUniqueId());
2054
2055 // Skip printing details if the caller requested a filter
2056 if (!job.shouldDump(filterUidFinal)) {
2057 continue;
2058 }
2059
2060 job.dump(pw, " ", true);
2061 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002062 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002063 pw.print(" (job=");
2064 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002065 pw.print(" user=");
2066 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002067 pw.print(" !pending=");
2068 pw.print(!mPendingJobs.contains(job));
2069 pw.print(" !active=");
2070 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002071 pw.print(" !backingup=");
2072 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002073 pw.print(" comp=");
2074 boolean componentPresent = false;
2075 try {
2076 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2077 job.getServiceComponent(),
2078 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2079 job.getUserId()) != null);
2080 } catch (RemoteException e) {
2081 }
2082 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002083 pw.println(")");
2084 }
Christopher Tate7060b042014-06-09 19:50:00 -07002085 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002086 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002087 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002088 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002089 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002090 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07002091 }
2092 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002093 pw.println("Uid priority overrides:");
2094 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002095 int uid = mUidPriorityOverride.keyAt(i);
2096 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2097 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2098 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2099 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002100 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002101 if (mBackingUpUids.size() > 0) {
2102 pw.println();
2103 pw.println("Backing up uids:");
2104 boolean first = true;
2105 for (int i = 0; i < mBackingUpUids.size(); i++) {
2106 int uid = mBackingUpUids.keyAt(i);
2107 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2108 if (first) {
2109 pw.print(" ");
2110 first = false;
2111 } else {
2112 pw.print(", ");
2113 }
2114 pw.print(UserHandle.formatUid(uid));
2115 }
2116 }
2117 pw.println();
2118 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002119 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002120 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002121 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002122 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2123 pw.println();
2124 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002125 pw.println("Pending queue:");
2126 for (int i=0; i<mPendingJobs.size(); i++) {
2127 JobStatus job = mPendingJobs.get(i);
2128 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2129 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002130 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002131 int priority = evaluateJobPriorityLocked(job);
2132 if (priority != JobInfo.PRIORITY_DEFAULT) {
2133 pw.print(" Evaluated priority: "); pw.println(priority);
2134 }
2135 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07002136 pw.print(" Enq: ");
2137 TimeUtils.formatDuration(now - job.madePending, pw);
2138 pw.println(" ago");
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002139 }
Christopher Tate7060b042014-06-09 19:50:00 -07002140 pw.println();
2141 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002142 for (int i=0; i<mActiveServices.size(); i++) {
2143 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002144 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Christopher Tate7234fc62017-04-03 17:36:07 -07002145 final JobStatus job = jsc.getRunningJob();
2146 if (job == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002147 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07002148 continue;
2149 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07002150 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002151 pw.print(" Running for: ");
2152 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
2153 pw.print(", timeout at: ");
2154 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
2155 pw.println();
Christopher Tate7234fc62017-04-03 17:36:07 -07002156 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002157 int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
2158 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002159 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002160 }
Christopher Tate7234fc62017-04-03 17:36:07 -07002161 pw.print(" Active at "); pw.println(job.madeActive);
2162 pw.print(" Pending for ");
2163 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2164 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07002165 }
2166 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002167 if (filterUid == -1) {
2168 pw.println();
2169 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2170 pw.print("mReportedActive="); pw.println(mReportedActive);
2171 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2172 }
Christopher Tate7060b042014-06-09 19:50:00 -07002173 }
2174 pw.println();
2175 }
2176}