blob: 46d7bfc06d84ae38a2eeb2410e87ad8db1284c0b [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;
Christopher Tate7060b042014-06-09 19:50:00 -070040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070042import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070043import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070046import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070047import android.content.pm.PackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070048import android.content.pm.ServiceInfo;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060049import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070050import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070051import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070052import android.os.BatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070053import android.os.Binder;
54import android.os.Handler;
55import android.os.Looper;
56import android.os.Message;
Shreyas Basargecbf5ae92016-03-08 16:13:06 +000057import android.os.Process;
Dianne Hackborn88e98df2015-03-23 13:29:14 -070058import android.os.PowerManager;
Christopher Tate7060b042014-06-09 19:50:00 -070059import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080060import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070061import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070062import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070063import android.os.SystemClock;
64import android.os.UserHandle;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070065import android.provider.Settings;
66import android.util.KeyValueListParser;
Christopher Tate7060b042014-06-09 19:50:00 -070067import android.util.Slog;
68import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080069import android.util.SparseIntArray;
70import android.util.TimeUtils;
Christopher Tate5d346052016-03-08 12:56:08 -080071
Dianne Hackbornfdb19562014-07-11 16:03:36 -070072import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070073import com.android.internal.app.procstats.ProcessStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070074import com.android.internal.util.ArrayUtils;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080075import com.android.server.DeviceIdleController;
76import com.android.server.LocalServices;
Christopher Tate2f36fd62016-02-18 18:36:08 -080077import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080078import com.android.server.job.controllers.AppIdleController;
Christopher Tate7060b042014-06-09 19:50:00 -070079import com.android.server.job.controllers.BatteryController;
80import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080081import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070082import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070083import com.android.server.job.controllers.IdleController;
84import com.android.server.job.controllers.JobStatus;
85import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070086import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -070087import com.android.server.job.controllers.TimeController;
88
Jeff Sharkey822cbd12016-02-25 11:09:55 -070089import libcore.util.EmptyArray;
90
Christopher Tate7060b042014-06-09 19:50:00 -070091/**
92 * Responsible for taking jobs representing work to be performed by a client app, and determining
93 * based on the criteria specified when that job should be run against the client application's
94 * endpoint.
95 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
96 * about constraints, or the state of active jobs. It receives callbacks from the various
97 * controllers and completed jobs and operates accordingly.
98 *
99 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
100 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
101 * @hide
102 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800103public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700104 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800105 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700106 public static final boolean DEBUG = false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800107
Dianne Hackborn970510b2016-02-24 16:56:42 -0800108 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700109 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800110 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800111 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800112 /** The maximum number of jobs that we allow an unprivileged app to schedule */
113 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700114
Christopher Tate2f36fd62016-02-18 18:36:08 -0800115
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800116 /** Global local for all job scheduler state. */
117 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700118 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700119 final JobStore mJobs;
Dianne Hackborn807de782016-04-07 17:54:41 -0700120 /** Tracking amount of time each package runs for. */
121 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700122
123 static final int MSG_JOB_EXPIRED = 0;
124 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700125 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000126 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700127
Christopher Tate7060b042014-06-09 19:50:00 -0700128 /**
129 * Track Services that have currently active or pending jobs. The index is provided by
130 * {@link JobStatus#getServiceToken()}
131 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700132 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700133 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700134 List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800135 /** Need direct access to this for testing. */
136 BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700137 /** Need direct access to this for testing. */
138 StorageController mStorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700139 /**
140 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
141 * when ready to execute them.
142 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700143 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700144
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700145 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700146
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700147 final JobHandler mHandler;
148 final JobSchedulerStub mJobSchedulerStub;
149
150 IBatteryStats mBatteryStats;
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700151 PowerManager mPowerManager;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800152 DeviceIdleController.LocalService mLocalDeviceIdleController;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700153
154 /**
155 * Set to true once we are allowed to run third party apps.
156 */
157 boolean mReadyToRock;
158
Christopher Tate7060b042014-06-09 19:50:00 -0700159 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800160 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800161 */
162 boolean mReportedActive;
163
164 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800165 * Current limit on the number of concurrent JobServiceContext entries we want to
166 * keep actively running a job.
167 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700168 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800169
170 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800171 * Which uids are currently in the foreground.
172 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800173 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
174
175 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
176
177 /**
178 * This array essentially stores the state of mActiveServices array.
179 * The ith index stores the job present on the ith JobServiceContext.
180 * We manipulate this array until we arrive at what jobs should be running on
181 * what JobServiceContext.
182 */
183 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
184 /**
185 * Indicates whether we need to act on this jobContext id
186 */
187 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
188 /**
189 * The uid whose jobs we would like to assign to a context.
190 */
191 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800192
193 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700194 * All times are in milliseconds. These constants are kept synchronized with the system
195 * global Settings. Any access to this class or its fields should be done while
196 * holding the JobSchedulerService.mLock lock.
197 */
198 private final class Constants extends ContentObserver {
199 // Key names stored in the settings value.
200 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
201 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800202 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700203 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700204 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
205 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
206 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
207 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
208 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
209 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
210 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
211 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
212 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
213 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
214
215 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
216 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800217 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700218 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700219 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
220 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
221 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
222 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
223 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
224 private static final int DEFAULT_FG_JOB_COUNT = 4;
225 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
226 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700227 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700228 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
229
230 /**
231 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
232 * early.
233 */
234 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
235 /**
236 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
237 * things early.
238 */
239 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
240 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800241 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
242 * schedule things early.
243 */
244 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
245 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700246 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
247 * schedule things early.
248 */
249 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
250 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700251 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
252 * things early. 1 == Run connectivity jobs as soon as ready.
253 */
254 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
255 /**
256 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
257 * schedule things early.
258 */
259 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
260 /**
261 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
262 * running some work early. This (and thus the other min counts) is now set to 1, to
263 * prevent any batching at this level. Since we now do batching through doze, that is
264 * a much better mechanism.
265 */
266 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
267 /**
268 * This is the job execution factor that is considered to be heavy use of the system.
269 */
270 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
271 /**
272 * This is the job execution factor that is considered to be moderate use of the system.
273 */
274 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
275 /**
276 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
277 */
278 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
279 /**
280 * The maximum number of background jobs we allow when the system is in a normal
281 * memory state.
282 */
283 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
284 /**
285 * The maximum number of background jobs we allow when the system is in a moderate
286 * memory state.
287 */
288 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
289 /**
290 * The maximum number of background jobs we allow when the system is in a low
291 * memory state.
292 */
293 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
294 /**
295 * The maximum number of background jobs we allow when the system is in a critical
296 * memory state.
297 */
298 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
299
300 private ContentResolver mResolver;
301 private final KeyValueListParser mParser = new KeyValueListParser(',');
302
303 public Constants(Handler handler) {
304 super(handler);
305 }
306
307 public void start(ContentResolver resolver) {
308 mResolver = resolver;
309 mResolver.registerContentObserver(Settings.Global.getUriFor(
310 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
311 updateConstants();
312 }
313
314 @Override
315 public void onChange(boolean selfChange, Uri uri) {
316 updateConstants();
317 }
318
319 private void updateConstants() {
320 synchronized (mLock) {
321 try {
322 mParser.setString(Settings.Global.getString(mResolver,
323 Settings.Global.ALARM_MANAGER_CONSTANTS));
324 } catch (IllegalArgumentException e) {
325 // Failed to parse the settings string, log this and move on
326 // with defaults.
327 Slog.e(TAG, "Bad device idle settings", e);
328 }
329
330 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
331 DEFAULT_MIN_IDLE_COUNT);
332 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
333 DEFAULT_MIN_CHARGING_COUNT);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800334 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
335 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700336 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
337 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700338 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
339 DEFAULT_MIN_CONNECTIVITY_COUNT);
340 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
341 DEFAULT_MIN_CONTENT_COUNT);
342 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
343 DEFAULT_MIN_READY_JOBS_COUNT);
344 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
345 DEFAULT_HEAVY_USE_FACTOR);
346 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
347 DEFAULT_MODERATE_USE_FACTOR);
348 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
349 DEFAULT_FG_JOB_COUNT);
350 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
351 DEFAULT_BG_NORMAL_JOB_COUNT);
352 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
353 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
354 }
355 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
356 DEFAULT_BG_MODERATE_JOB_COUNT);
357 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
358 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
359 }
360 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
361 DEFAULT_BG_LOW_JOB_COUNT);
362 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
363 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
364 }
365 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
366 DEFAULT_BG_CRITICAL_JOB_COUNT);
367 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
368 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
369 }
370 }
371 }
372
373 void dump(PrintWriter pw) {
374 pw.println(" Settings:");
375
376 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
377 pw.print(MIN_IDLE_COUNT); pw.println();
378
379 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
380 pw.print(MIN_CHARGING_COUNT); pw.println();
381
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800382 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
383 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
384
Dianne Hackborn532ea262017-03-17 17:50:55 -0700385 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
386 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
387
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700388 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
389 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
390
391 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
392 pw.print(MIN_CONTENT_COUNT); pw.println();
393
394 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
395 pw.print(MIN_READY_JOBS_COUNT); pw.println();
396
397 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
398 pw.print(HEAVY_USE_FACTOR); pw.println();
399
400 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
401 pw.print(MODERATE_USE_FACTOR); pw.println();
402
403 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
404 pw.print(FG_JOB_COUNT); pw.println();
405
406 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
407 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
408
409 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
410 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
411
412 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
413 pw.print(BG_LOW_JOB_COUNT); pw.println();
414
415 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
416 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
417 }
418 }
419
420 final Constants mConstants;
421
422 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700423 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
424 * still clean up. On reinstall the package will have a new uid.
425 */
426 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
427 @Override
428 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700429 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700430 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700431 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700432 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700433 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700434 // Purge the app's jobs if the whole package was just disabled. When this is
435 // the case the component name will be a bare package name.
436 final String pkgName = getPackageName(intent);
437 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
438 if (pkgName != null && pkgUid != -1) {
439 final String[] changedComponents = intent.getStringArrayExtra(
440 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
441 if (changedComponents != null) {
442 for (String component : changedComponents) {
443 if (component.equals(pkgName)) {
444 if (DEBUG) {
445 Slog.d(TAG, "Package state change: " + pkgName);
446 }
447 try {
448 final int userId = UserHandle.getUserId(pkgUid);
449 IPackageManager pm = AppGlobals.getPackageManager();
450 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
451 if (state == COMPONENT_ENABLED_STATE_DISABLED
452 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
453 if (DEBUG) {
454 Slog.d(TAG, "Removing jobs for package " + pkgName
455 + " in user " + userId);
456 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800457 cancelJobsForUid(pkgUid);
Christopher Tateb5c07882016-05-26 17:11:09 -0700458 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700459 } catch (RemoteException|IllegalArgumentException e) {
460 /*
461 * IllegalArgumentException means that the package doesn't exist.
462 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
463 * behind outright uninstall, so by the time we try to act it's gone.
464 * We don't need to act on this PACKAGE_CHANGED when this happens;
465 * we'll get a PACKAGE_REMOVED later and clean up then.
466 *
467 * RemoteException can't actually happen; the package manager is
468 * running in this same process.
469 */
470 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700471 break;
472 }
473 }
474 }
475 } else {
476 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
477 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700478 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700479 // If this is an outright uninstall rather than the first half of an
480 // app update sequence, cancel the jobs associated with the app.
481 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
482 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
483 if (DEBUG) {
484 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
485 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800486 cancelJobsForUid(uidRemoved);
Christopher Tate7060b042014-06-09 19:50:00 -0700487 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700488 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700489 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
490 if (DEBUG) {
491 Slog.d(TAG, "Removing jobs for user: " + userId);
492 }
493 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700494 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
495 // Has this package scheduled any jobs, such that we will take action
496 // if it were to be force-stopped?
497 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
498 final String pkgName = intent.getData().getSchemeSpecificPart();
499 if (pkgUid != -1) {
500 List<JobStatus> jobsForUid;
501 synchronized (mLock) {
502 jobsForUid = mJobs.getJobsByUid(pkgUid);
503 }
504 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
505 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
506 if (DEBUG) {
507 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
508 + pkgUid + " has jobs");
509 }
510 setResultCode(Activity.RESULT_OK);
511 break;
512 }
513 }
514 }
515 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
516 // possible force-stop
517 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
518 final String pkgName = intent.getData().getSchemeSpecificPart();
519 if (pkgUid != -1) {
520 if (DEBUG) {
521 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
522 }
523 cancelJobsForPackageAndUid(pkgName, pkgUid);
524 }
Christopher Tate7060b042014-06-09 19:50:00 -0700525 }
526 }
527 };
528
Christopher Tateb5c07882016-05-26 17:11:09 -0700529 private String getPackageName(Intent intent) {
530 Uri uri = intent.getData();
531 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
532 return pkg;
533 }
534
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700535 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Sudheer Shanka80255802017-03-04 14:48:53 -0800536 @Override public void onUidStateChanged(int uid, int procState,
537 long procStateSeq) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800538 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700539 }
540
Dianne Hackborne07641d2016-11-09 15:07:23 -0800541 @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800542 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800543 if (disabled) {
544 cancelJobsForUid(uid);
545 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700546 }
547
548 @Override public void onUidActive(int uid) throws RemoteException {
549 }
550
Dianne Hackborne07641d2016-11-09 15:07:23 -0800551 @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
552 if (disabled) {
553 cancelJobsForUid(uid);
554 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700555 }
556 };
557
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800558 public Object getLock() {
559 return mLock;
560 }
561
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700562 public JobStore getJobStore() {
563 return mJobs;
564 }
565
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700566 @Override
567 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700568 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
569 // Let's kick any outstanding jobs for this user.
570 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
571 }
572
573 @Override
574 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700575 // Let's kick any outstanding jobs for this user.
576 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
577 }
578
579 @Override
580 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700581 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700582 }
583
Christopher Tate7060b042014-06-09 19:50:00 -0700584 /**
585 * Entry point from client to schedule the provided job.
586 * This cancels the job if it's already been scheduled, and replaces it with the one provided.
587 * @param job JobInfo object containing execution parameters
588 * @param uId The package identifier of the application this job is for.
Christopher Tate7060b042014-06-09 19:50:00 -0700589 * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
590 */
Matthew Williams900c67f2014-07-09 12:46:53 -0700591 public int schedule(JobInfo job, int uId) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800592 return scheduleAsPackage(job, uId, null, -1, null);
Shreyas Basarge968ac752016-01-11 23:09:26 +0000593 }
594
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800595 public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
596 String tag) {
597 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700598 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800599 if (ActivityManager.getService().isAppStartModeDisabled(uId,
600 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700601 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
602 + " -- package not allowed to start");
603 return JobScheduler.RESULT_FAILURE;
604 }
605 } catch (RemoteException e) {
606 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800607 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
608 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800609 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800610 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800611 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800612 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
613 Slog.w(TAG, "Too many jobs for uid " + uId);
614 throw new IllegalStateException("Apps may not schedule more than "
615 + MAX_JOBS_PER_APP + " distinct jobs");
616 }
617 }
618
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800619 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700620 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700621 cancelJobImpl(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700622 }
623 startTrackingJob(jobStatus, toCancel);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800624 }
Matthew Williamsbafeeb92014-08-08 11:51:06 -0700625 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700626 return JobScheduler.RESULT_SUCCESS;
627 }
628
629 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800630 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800631 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
632 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
633 for (int i = jobs.size() - 1; i >= 0; i--) {
634 JobStatus job = jobs.get(i);
635 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700636 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800637 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700638 }
Christopher Tate7060b042014-06-09 19:50:00 -0700639 }
640
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600641 public JobInfo getPendingJob(int uid, int jobId) {
642 synchronized (mLock) {
643 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
644 for (int i = jobs.size() - 1; i >= 0; i--) {
645 JobStatus job = jobs.get(i);
646 if (job.getJobId() == jobId) {
647 return job.getJob();
648 }
649 }
650 return null;
651 }
652 }
653
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700654 void cancelJobsForUser(int userHandle) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700655 List<JobStatus> jobsForUser;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800656 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700657 jobsForUser = mJobs.getJobsByUser(userHandle);
658 }
659 for (int i=0; i<jobsForUser.size(); i++) {
660 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700661 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700662 }
663 }
664
Christopher Tateee7805b2016-07-15 16:56:56 -0700665 void cancelJobsForPackageAndUid(String pkgName, int uid) {
666 List<JobStatus> jobsForUid;
667 synchronized (mLock) {
668 jobsForUid = mJobs.getJobsByUid(uid);
669 }
670 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
671 final JobStatus job = jobsForUid.get(i);
672 if (job.getSourcePackageName().equals(pkgName)) {
673 cancelJobImpl(job, null);
674 }
675 }
676 }
677
Christopher Tate7060b042014-06-09 19:50:00 -0700678 /**
679 * Entry point from client to cancel all jobs originating from their uid.
680 * This will remove the job from the master list, and cancel the job if it was staged for
681 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700682 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800683 *
Christopher Tate7060b042014-06-09 19:50:00 -0700684 */
Dianne Hackborne07641d2016-11-09 15:07:23 -0800685 public void cancelJobsForUid(int uid) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700686 List<JobStatus> jobsForUid;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800687 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700688 jobsForUid = mJobs.getJobsByUid(uid);
689 }
690 for (int i=0; i<jobsForUid.size(); i++) {
691 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700692 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700693 }
694 }
695
696 /**
697 * Entry point from client to cancel the job corresponding to the jobId provided.
698 * This will remove the job from the master list, and cancel the job if it was staged for
699 * execution or being executed.
700 * @param uid Uid of the calling client.
701 * @param jobId Id of the job, provided at schedule-time.
702 */
703 public void cancelJob(int uid, int jobId) {
704 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800705 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700706 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Matthew Williams48a30db2014-09-23 13:39:36 -0700707 }
708 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700709 cancelJobImpl(toCancel, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700710 }
711 }
712
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700713 private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800714 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700715 stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800716 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700717 // Remove from pending queue.
Dianne Hackborn807de782016-04-07 17:54:41 -0700718 if (mPendingJobs.remove(cancelled)) {
719 mJobPackageTracker.noteNonpending(cancelled);
720 }
Matthew Williams48a30db2014-09-23 13:39:36 -0700721 // Cancel if running.
Shreyas Basarge5db09082016-01-07 13:38:29 +0000722 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800723 reportActive();
Matthew Williams48a30db2014-09-23 13:39:36 -0700724 }
Christopher Tate7060b042014-06-09 19:50:00 -0700725 }
726
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800727 void updateUidState(int uid, int procState) {
728 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800729 if (procState == ActivityManager.PROCESS_STATE_TOP) {
730 // Only use this if we are exactly the top app. All others can live
731 // with just the foreground priority. This means that persistent processes
732 // can never be the top app priority... that is fine.
733 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
734 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
735 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800736 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800737 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800738 }
739 }
740 }
741
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700742 @Override
743 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800744 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700745 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -0600746 // When becoming idle, make sure no jobs are actively running,
747 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700748 for (int i=0; i<mActiveServices.size(); i++) {
749 JobServiceContext jsc = mActiveServices.get(i);
750 final JobStatus executing = jsc.getRunningJob();
Jeff Sharkey34618b52016-06-01 15:51:19 -0600751 if (executing != null
752 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700753 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
754 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700755 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700756 } else {
757 // When coming out of idle, allow thing to start back up.
758 if (mReadyToRock) {
759 if (mLocalDeviceIdleController != null) {
760 if (!mReportedActive) {
761 mReportedActive = true;
762 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700763 }
764 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700765 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700766 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700767 }
768 }
769 }
770
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800771 void reportActive() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000772 // active is true if pending queue contains jobs OR some job is running.
773 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800774 if (mPendingJobs.size() <= 0) {
775 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700776 final JobServiceContext jsc = mActiveServices.get(i);
777 final JobStatus job = jsc.getRunningJob();
778 if (job != null
779 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
780 && !job.dozeWhitelisted) {
781 // We will report active if we have a job running and it is not an exception
782 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800783 active = true;
784 break;
785 }
786 }
787 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000788
789 if (mReportedActive != active) {
790 mReportedActive = active;
791 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800792 mLocalDeviceIdleController.setJobsActive(active);
793 }
794 }
795 }
796
Christopher Tate7060b042014-06-09 19:50:00 -0700797 /**
798 * Initializes the system service.
799 * <p>
800 * Subclasses must define a single argument constructor that accepts the context
801 * and passes it to super.
802 * </p>
803 *
804 * @param context The system server context.
805 */
806 public JobSchedulerService(Context context) {
807 super(context);
Dianne Hackborn970e3f42016-06-01 10:55:13 -0700808 mHandler = new JobHandler(context.getMainLooper());
809 mConstants = new Constants(mHandler);
810 mJobSchedulerStub = new JobSchedulerStub();
811 mJobs = JobStore.initAndGet(this);
812
Christopher Tate7060b042014-06-09 19:50:00 -0700813 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700814 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700815 mControllers.add(ConnectivityController.get(this));
816 mControllers.add(TimeController.get(this));
817 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800818 mBatteryController = BatteryController.get(this);
819 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700820 mStorageController = StorageController.get(this);
821 mControllers.add(mStorageController);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800822 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800823 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700824 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700825 }
826
827 @Override
828 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000829 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700830 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
831 }
832
833 @Override
834 public void onBootPhase(int phase) {
835 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700836 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000837 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700838 final IntentFilter filter = new IntentFilter();
839 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
840 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -0700841 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
842 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -0700843 filter.addDataScheme("package");
844 getContext().registerReceiverAsUser(
845 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
846 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
847 getContext().registerReceiverAsUser(
848 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000849 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700850 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800851 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800852 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800853 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
854 null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700855 } catch (RemoteException e) {
856 // ignored; both services live in system_server
857 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700858 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800859 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700860 // Let's go!
861 mReadyToRock = true;
862 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
863 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800864 mLocalDeviceIdleController
865 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700866 // Create the "runners".
867 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
868 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700869 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700870 getContext().getMainLooper()));
871 }
872 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800873 mJobs.forEachJob(new JobStatusFunctor() {
874 @Override
875 public void process(JobStatus job) {
876 for (int controller = 0; controller < mControllers.size(); controller++) {
877 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800878 sc.maybeStartTrackingJobLocked(job, null);
879 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700880 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800881 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700882 // GO GO GO!
883 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
884 }
Christopher Tate7060b042014-06-09 19:50:00 -0700885 }
886 }
887
888 /**
889 * Called when we have a job status object that we need to insert in our
890 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
891 * about.
892 */
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800893 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800894 synchronized (mLock) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800895 final boolean update = mJobs.add(jobStatus);
896 if (mReadyToRock) {
897 for (int i = 0; i < mControllers.size(); i++) {
898 StateController controller = mControllers.get(i);
899 if (update) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700900 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800901 }
902 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700903 }
Christopher Tate7060b042014-06-09 19:50:00 -0700904 }
Christopher Tate7060b042014-06-09 19:50:00 -0700905 }
906 }
907
908 /**
909 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
910 * object removed.
911 */
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700912 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
913 boolean writeBack) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800914 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700915 // Remove from store as well as controllers.
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800916 final boolean removed = mJobs.remove(jobStatus, writeBack);
917 if (removed && mReadyToRock) {
918 for (int i=0; i<mControllers.size(); i++) {
919 StateController controller = mControllers.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700920 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800921 }
Christopher Tate7060b042014-06-09 19:50:00 -0700922 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800923 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -0700924 }
Christopher Tate7060b042014-06-09 19:50:00 -0700925 }
926
Shreyas Basarge5db09082016-01-07 13:38:29 +0000927 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700928 for (int i=0; i<mActiveServices.size(); i++) {
929 JobServiceContext jsc = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700930 final JobStatus executing = jsc.getRunningJob();
931 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Shreyas Basarge5db09082016-01-07 13:38:29 +0000932 jsc.cancelExecutingJob(reason);
Christopher Tate7060b042014-06-09 19:50:00 -0700933 return true;
934 }
935 }
936 return false;
937 }
938
939 /**
940 * @param job JobStatus we are querying against.
941 * @return Whether or not the job represented by the status object is currently being run or
942 * is pending.
943 */
944 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700945 for (int i=0; i<mActiveServices.size(); i++) {
946 JobServiceContext serviceContext = mActiveServices.get(i);
Christopher Tateeafb5352016-10-04 16:34:48 -0700947 // The 'unsafe' direct-internal-reference running-job inspector is okay to
948 // use here because we are already holding the necessary lock *and* we
949 // immediately discard the returned object reference, if any; we return
950 // only a boolean state indicator to the caller.
951 final JobStatus running = serviceContext.getRunningJobUnsafeLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700952 if (running != null && running.matches(job.getUid(), job.getJobId())) {
953 return true;
954 }
955 }
956 return false;
957 }
958
Dianne Hackborn807de782016-04-07 17:54:41 -0700959 void noteJobsPending(List<JobStatus> jobs) {
960 for (int i = jobs.size() - 1; i >= 0; i--) {
961 JobStatus job = jobs.get(i);
962 mJobPackageTracker.notePending(job);
963 }
964 }
965
966 void noteJobsNonpending(List<JobStatus> jobs) {
967 for (int i = jobs.size() - 1; i >= 0; i--) {
968 JobStatus job = jobs.get(i);
969 mJobPackageTracker.noteNonpending(job);
970 }
971 }
972
Christopher Tate7060b042014-06-09 19:50:00 -0700973 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700974 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
975 * specify an override deadline on a failed job (the failed job will run even though it's not
976 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
977 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
978 *
Christopher Tate7060b042014-06-09 19:50:00 -0700979 * @param failureToReschedule Provided job status that we will reschedule.
980 * @return A newly instantiated JobStatus with the same constraints as the last job except
981 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700982 *
983 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
Christopher Tate7060b042014-06-09 19:50:00 -0700984 */
985 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
986 final long elapsedNowMillis = SystemClock.elapsedRealtime();
987 final JobInfo job = failureToReschedule.getJob();
988
989 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -0700990 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
991 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -0700992
993 switch (job.getBackoffPolicy()) {
Matthew Williamsd1c06752014-08-22 14:15:28 -0700994 case JobInfo.BACKOFF_POLICY_LINEAR:
995 delayMillis = initialBackoffMillis * backoffAttempts;
Christopher Tate7060b042014-06-09 19:50:00 -0700996 break;
997 default:
998 if (DEBUG) {
999 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1000 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001001 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
1002 delayMillis =
1003 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
Christopher Tate7060b042014-06-09 19:50:00 -07001004 break;
1005 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001006 delayMillis =
1007 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001008 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -07001009 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001010 for (int ic=0; ic<mControllers.size(); ic++) {
1011 StateController controller = mControllers.get(ic);
1012 controller.rescheduleForFailure(newJob, failureToReschedule);
1013 }
1014 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001015 }
1016
1017 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001018 * Called after a periodic has executed so we can reschedule it. We take the last execution
1019 * time of the job to be the time of completion (i.e. the time at which this function is
1020 * called).
Christopher Tate7060b042014-06-09 19:50:00 -07001021 * This could be inaccurate b/c the job can run for as long as
1022 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1023 * to underscheduling at least, rather than if we had taken the last execution time to be the
1024 * start of the execution.
1025 * @return A new job representing the execution criteria for this instantiation of the
1026 * recurring job.
1027 */
1028 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1029 final long elapsedNow = SystemClock.elapsedRealtime();
1030 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001031 long runEarly = 0L;
1032
1033 // If this periodic was rescheduled it won't have a deadline.
1034 if (periodicToReschedule.hasDeadlineConstraint()) {
1035 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1036 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001037 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001038 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001039 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1040 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001041
1042 if (DEBUG) {
1043 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1044 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1045 }
1046 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1047 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1048 }
1049
1050 // JobCompletedListener implementations.
1051
1052 /**
1053 * A job just finished executing. We fetch the
1054 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1055 * whether we want to reschedule we readd it to the controllers.
1056 * @param jobStatus Completed job.
1057 * @param needsReschedule Whether the implementing class should reschedule this job.
1058 */
1059 @Override
1060 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
1061 if (DEBUG) {
1062 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1063 }
Shreyas Basarge73f10252016-02-11 17:06:13 +00001064 // Do not write back immediately if this is a periodic job. The job may get lost if system
1065 // shuts down before it is added back.
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001066 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001067 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001068 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001069 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001070 // We still want to check for jobs to execute, because this job may have
1071 // scheduled a new job under the same job id, and now we can run it.
1072 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001073 return;
1074 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001075 // Note: there is a small window of time in here where, when rescheduling a job,
1076 // we will stop monitoring its content providers. This should be fixed by stopping
1077 // the old job after scheduling the new one, but since we have no lock held here
1078 // that may cause ordering problems if the app removes jobStatus while in here.
Christopher Tate7060b042014-06-09 19:50:00 -07001079 if (needsReschedule) {
1080 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001081 startTrackingJob(rescheduled, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001082 } else if (jobStatus.getJob().isPeriodic()) {
1083 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001084 startTrackingJob(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001085 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001086 reportActive();
1087 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001088 }
1089
1090 // StateChangedListener implementations.
1091
1092 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001093 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1094 * some controller's state has changed, so as to run through the list of jobs and start/stop
1095 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001096 */
1097 @Override
1098 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001099 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001100 }
1101
1102 @Override
1103 public void onRunJobNow(JobStatus jobStatus) {
1104 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1105 }
1106
Christopher Tate7060b042014-06-09 19:50:00 -07001107 private class JobHandler extends Handler {
1108
1109 public JobHandler(Looper looper) {
1110 super(looper);
1111 }
1112
1113 @Override
1114 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001115 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001116 if (!mReadyToRock) {
1117 return;
1118 }
1119 }
Christopher Tate7060b042014-06-09 19:50:00 -07001120 switch (message.what) {
1121 case MSG_JOB_EXPIRED:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001122 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001123 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001124 // runNow can be null, which is a controller's way of indicating that its
1125 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001126 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001127 mJobPackageTracker.notePending(runNow);
Christopher Tate7060b042014-06-09 19:50:00 -07001128 mPendingJobs.add(runNow);
1129 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001130 queueReadyJobsForExecutionLockedH();
Christopher Tate7060b042014-06-09 19:50:00 -07001131 }
Christopher Tate7060b042014-06-09 19:50:00 -07001132 break;
1133 case MSG_CHECK_JOB:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001134 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001135 if (mReportedActive) {
1136 // if jobs are currently being run, queue all ready jobs for execution.
1137 queueReadyJobsForExecutionLockedH();
1138 } else {
1139 // Check the list of jobs and run some of them if we feel inclined.
1140 maybeQueueReadyJobsForExecutionLockedH();
1141 }
1142 }
1143 break;
1144 case MSG_CHECK_JOB_GREEDY:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001145 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001146 queueReadyJobsForExecutionLockedH();
Matthew Williams48a30db2014-09-23 13:39:36 -07001147 }
Christopher Tate7060b042014-06-09 19:50:00 -07001148 break;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001149 case MSG_STOP_JOB:
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001150 cancelJobImpl((JobStatus)message.obj, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001151 break;
Christopher Tate7060b042014-06-09 19:50:00 -07001152 }
1153 maybeRunPendingJobsH();
1154 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1155 removeMessages(MSG_CHECK_JOB);
1156 }
1157
1158 /**
1159 * Run through list of jobs and execute all possible - at least one is expired so we do
1160 * as many as we can.
1161 */
Matthew Williams48a30db2014-09-23 13:39:36 -07001162 private void queueReadyJobsForExecutionLockedH() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001163 if (DEBUG) {
1164 Slog.d(TAG, "queuing all ready jobs for execution:");
1165 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001166 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001167 mPendingJobs.clear();
1168 mJobs.forEachJob(mReadyQueueFunctor);
1169 mReadyQueueFunctor.postProcess();
1170
Matthew Williams48a30db2014-09-23 13:39:36 -07001171 if (DEBUG) {
1172 final int queuedJobs = mPendingJobs.size();
1173 if (queuedJobs == 0) {
1174 Slog.d(TAG, "No jobs pending.");
1175 } else {
1176 Slog.d(TAG, queuedJobs + " jobs queued.");
Matthew Williams75fc5252014-09-02 16:17:53 -07001177 }
Christopher Tate7060b042014-06-09 19:50:00 -07001178 }
1179 }
1180
Christopher Tate2f36fd62016-02-18 18:36:08 -08001181 class ReadyJobQueueFunctor implements JobStatusFunctor {
1182 ArrayList<JobStatus> newReadyJobs;
1183
1184 @Override
1185 public void process(JobStatus job) {
1186 if (isReadyToBeExecutedLocked(job)) {
1187 if (DEBUG) {
1188 Slog.d(TAG, " queued " + job.toShortString());
1189 }
1190 if (newReadyJobs == null) {
1191 newReadyJobs = new ArrayList<JobStatus>();
1192 }
1193 newReadyJobs.add(job);
1194 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1195 stopJobOnServiceContextLocked(job,
1196 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1197 }
1198 }
1199
1200 public void postProcess() {
1201 if (newReadyJobs != null) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001202 noteJobsPending(newReadyJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001203 mPendingJobs.addAll(newReadyJobs);
1204 }
1205 newReadyJobs = null;
1206 }
1207 }
1208 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1209
Christopher Tate7060b042014-06-09 19:50:00 -07001210 /**
1211 * The state of at least one job has changed. Here is where we could enforce various
1212 * policies on when we want to execute jobs.
1213 * Right now the policy is such:
1214 * If >1 of the ready jobs is idle mode we send all of them off
1215 * if more than 2 network connectivity jobs are ready we send them all off.
1216 * If more than 4 jobs total are ready we send them all off.
1217 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1218 */
Christopher Tate2f36fd62016-02-18 18:36:08 -08001219 class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1220 int chargingCount;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001221 int batteryNotLowCount;
Dianne Hackborn532ea262017-03-17 17:50:55 -07001222 int storageNotLowCount;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001223 int idleCount;
1224 int backoffCount;
1225 int connectivityCount;
1226 int contentCount;
1227 List<JobStatus> runnableJobs;
1228
1229 public MaybeReadyJobQueueFunctor() {
1230 reset();
1231 }
1232
1233 // Functor method invoked for each job via JobStore.forEachJob()
1234 @Override
1235 public void process(JobStatus job) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001236 if (isReadyToBeExecutedLocked(job)) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001237 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001238 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1239 job.getJob().getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001240 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1241 + job.getJob().toString() + " -- package not allowed to start");
1242 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001243 return;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001244 }
1245 } catch (RemoteException e) {
1246 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001247 if (job.getNumFailures() > 0) {
1248 backoffCount++;
Christopher Tate7060b042014-06-09 19:50:00 -07001249 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001250 if (job.hasIdleConstraint()) {
1251 idleCount++;
1252 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001253 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1254 || job.hasNotRoamingConstraint()) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001255 connectivityCount++;
1256 }
1257 if (job.hasChargingConstraint()) {
1258 chargingCount++;
1259 }
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001260 if (job.hasBatteryNotLowConstraint()) {
1261 batteryNotLowCount++;
1262 }
Dianne Hackborn532ea262017-03-17 17:50:55 -07001263 if (job.hasStorageNotLowConstraint()) {
1264 storageNotLowCount++;
1265 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001266 if (job.hasContentTriggerConstraint()) {
1267 contentCount++;
1268 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001269 if (runnableJobs == null) {
1270 runnableJobs = new ArrayList<>();
1271 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001272 runnableJobs.add(job);
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001273 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001274 stopJobOnServiceContextLocked(job,
1275 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001276 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001277 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001278
1279 public void postProcess() {
1280 if (backoffCount > 0 ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001281 idleCount >= mConstants.MIN_IDLE_COUNT ||
1282 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1283 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001284 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
Dianne Hackborn532ea262017-03-17 17:50:55 -07001285 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001286 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1287 (runnableJobs != null
1288 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001289 if (DEBUG) {
1290 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1291 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001292 noteJobsPending(runnableJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001293 mPendingJobs.addAll(runnableJobs);
1294 } else {
1295 if (DEBUG) {
1296 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1297 }
Christopher Tate7060b042014-06-09 19:50:00 -07001298 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001299
1300 // Be ready for next time
1301 reset();
Matthew Williams48a30db2014-09-23 13:39:36 -07001302 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001303
1304 private void reset() {
1305 chargingCount = 0;
1306 idleCount = 0;
1307 backoffCount = 0;
1308 connectivityCount = 0;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001309 batteryNotLowCount = 0;
Dianne Hackborn532ea262017-03-17 17:50:55 -07001310 storageNotLowCount = 0;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001311 contentCount = 0;
1312 runnableJobs = null;
1313 }
1314 }
1315 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1316
1317 private void maybeQueueReadyJobsForExecutionLockedH() {
1318 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1319
Dianne Hackborn807de782016-04-07 17:54:41 -07001320 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001321 mPendingJobs.clear();
1322 mJobs.forEachJob(mMaybeQueueFunctor);
1323 mMaybeQueueFunctor.postProcess();
Christopher Tate7060b042014-06-09 19:50:00 -07001324 }
1325
1326 /**
1327 * Criteria for moving a job into the pending queue:
1328 * - It's ready.
1329 * - It's not pending.
1330 * - It's not already running on a JSC.
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001331 * - The user that requested the job is running.
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001332 * - The component is enabled and runnable.
Christopher Tate7060b042014-06-09 19:50:00 -07001333 */
1334 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Christopher Tateb1d64482017-03-15 12:01:22 -07001335 final boolean jobExists = mJobs.containsJob(job);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001336 final boolean jobReady = job.isReady();
1337 final boolean jobPending = mPendingJobs.contains(job);
1338 final boolean jobActive = isCurrentlyActiveLocked(job);
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001339
1340 final int userId = job.getUserId();
1341 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
Christopher Tateb1d64482017-03-15 12:01:22 -07001342
1343 if (DEBUG) {
1344 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1345 + " exists=" + jobExists
1346 + " ready=" + jobReady + " pending=" + jobPending
1347 + " active=" + jobActive + " userStarted=" + userStarted);
1348 }
1349
1350 // Short circuit: don't do the expensive PM check unless we really think
1351 // we might need to run this job now.
1352 if (!jobExists || !userStarted || !jobReady || jobPending || jobActive) {
1353 return false;
1354 }
1355
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001356 final boolean componentPresent;
1357 try {
1358 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1359 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1360 userId) != null);
1361 } catch (RemoteException e) {
1362 throw e.rethrowAsRuntimeException();
1363 }
1364
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001365 if (DEBUG) {
1366 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001367 + " componentPresent=" + componentPresent);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001368 }
Christopher Tateb1d64482017-03-15 12:01:22 -07001369
1370 // Everything else checked out so far, so this is the final yes/no check
1371 return componentPresent;
Christopher Tate7060b042014-06-09 19:50:00 -07001372 }
1373
1374 /**
1375 * Criteria for cancelling an active job:
1376 * - It's not ready
1377 * - It's running on a JSC.
1378 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001379 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
Christopher Tate7060b042014-06-09 19:50:00 -07001380 return !job.isReady() && isCurrentlyActiveLocked(job);
1381 }
1382
1383 /**
1384 * Reconcile jobs in the pending queue against available execution contexts.
1385 * A controller can force a job into the pending queue even if it's already running, but
1386 * here is where we decide whether to actually execute it.
1387 */
1388 private void maybeRunPendingJobsH() {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001389 synchronized (mLock) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001390 if (DEBUG) {
1391 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1392 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001393 assignJobsToContextsLocked();
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001394 reportActive();
Christopher Tate7060b042014-06-09 19:50:00 -07001395 }
1396 }
1397 }
1398
Dianne Hackborn807de782016-04-07 17:54:41 -07001399 private int adjustJobPriority(int curPriority, JobStatus job) {
1400 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1401 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001402 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001403 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001404 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001405 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1406 }
1407 }
1408 return curPriority;
1409 }
1410
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001411 private int evaluateJobPriorityLocked(JobStatus job) {
1412 int priority = job.getPriority();
1413 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001414 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001415 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001416 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1417 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001418 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001419 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001420 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001421 }
1422
Christopher Tate7060b042014-06-09 19:50:00 -07001423 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001424 * Takes jobs from pending queue and runs them on available contexts.
1425 * If no contexts are available, preempts lower priority jobs to
1426 * run higher priority ones.
1427 * Lock on mJobs before calling this function.
1428 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001429 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001430 if (DEBUG) {
1431 Slog.d(TAG, printPendingQueue());
1432 }
1433
Dianne Hackborn970510b2016-02-24 16:56:42 -08001434 int memLevel;
1435 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001436 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001437 } catch (RemoteException e) {
1438 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1439 }
1440 switch (memLevel) {
1441 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001442 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001443 break;
1444 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001445 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001446 break;
1447 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001448 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001449 break;
1450 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001451 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001452 break;
1453 }
1454
1455 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1456 boolean[] act = mTmpAssignAct;
1457 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1458 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001459 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001460 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1461 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001462 final JobStatus status = js.getRunningJob();
1463 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001464 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001465 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1466 numForeground++;
1467 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001468 }
1469 act[i] = false;
1470 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001471 }
1472 if (DEBUG) {
1473 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1474 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001475 for (int i=0; i<mPendingJobs.size(); i++) {
1476 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001477
1478 // If job is already running, go to next job.
1479 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1480 if (jobRunningContext != -1) {
1481 continue;
1482 }
1483
Dianne Hackborn970510b2016-02-24 16:56:42 -08001484 final int priority = evaluateJobPriorityLocked(nextPending);
1485 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001486
Shreyas Basarge5db09082016-01-07 13:38:29 +00001487 // Find a context for nextPending. The context should be available OR
1488 // it should have lowest priority among all running jobs
1489 // (sharing the same Uid as nextPending)
1490 int minPriority = Integer.MAX_VALUE;
1491 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001492 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1493 JobStatus job = contextIdToJobMap[j];
1494 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001495 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001496 if ((numActive < mMaxActiveJobs ||
1497 (priority >= JobInfo.PRIORITY_TOP_APP &&
1498 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001499 (preferredUid == nextPending.getUid() ||
1500 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1501 // This slot is free, and we haven't yet hit the limit on
1502 // concurrent jobs... we can just throw the job in to here.
1503 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001504 break;
1505 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001506 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001507 // the context has a preferred Uid or we have reached the limit on
1508 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001509 continue;
1510 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001511 if (job.getUid() != nextPending.getUid()) {
1512 continue;
1513 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001514 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001515 continue;
1516 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001517 if (minPriority > nextPending.lastEvaluatedPriority) {
1518 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001519 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001520 }
1521 }
1522 if (minPriorityContextId != -1) {
1523 contextIdToJobMap[minPriorityContextId] = nextPending;
1524 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001525 numActive++;
1526 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1527 numForeground++;
1528 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001529 }
1530 }
1531 if (DEBUG) {
1532 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1533 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001534 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001535 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001536 boolean preservePreferredUid = false;
1537 if (act[i]) {
1538 JobStatus js = mActiveServices.get(i).getRunningJob();
1539 if (js != null) {
1540 if (DEBUG) {
1541 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1542 }
1543 // preferredUid will be set to uid of currently running job.
1544 mActiveServices.get(i).preemptExecutingJob();
1545 preservePreferredUid = true;
1546 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001547 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001548 if (DEBUG) {
1549 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001550 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001551 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001552 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001553 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001554 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001555 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1556 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001557 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001558 if (mPendingJobs.remove(pendingJob)) {
1559 mJobPackageTracker.noteNonpending(pendingJob);
1560 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001561 }
1562 }
1563 if (!preservePreferredUid) {
1564 mActiveServices.get(i).clearPreferredUid();
1565 }
1566 }
1567 }
1568
1569 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1570 for (int i=0; i<map.length; i++) {
1571 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1572 return i;
1573 }
1574 }
1575 return -1;
1576 }
1577
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001578 final class LocalService implements JobSchedulerInternal {
1579
1580 /**
1581 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1582 * jobs are always considered pending.
1583 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001584 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001585 public List<JobInfo> getSystemScheduledPendingJobs() {
1586 synchronized (mLock) {
1587 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1588 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1589 @Override
1590 public void process(JobStatus job) {
1591 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1592 pendingJobs.add(job.getJob());
1593 }
1594 }
1595 });
1596 return pendingJobs;
1597 }
1598 }
1599 }
1600
Shreyas Basarge5db09082016-01-07 13:38:29 +00001601 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001602 * Binder stub trampoline implementation
1603 */
1604 final class JobSchedulerStub extends IJobScheduler.Stub {
1605 /** Cache determination of whether a given app can persist jobs
1606 * key is uid of the calling app; value is undetermined/true/false
1607 */
1608 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1609
1610 // Enforce that only the app itself (or shared uid participant) can schedule a
1611 // job that runs one of the app's services, as well as verifying that the
1612 // named service properly requires the BIND_JOB_SERVICE permission
1613 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001614 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001615 final ComponentName service = job.getService();
1616 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001617 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001618 PackageManager.MATCH_DIRECT_BOOT_AWARE
1619 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001620 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001621 if (si == null) {
1622 throw new IllegalArgumentException("No such service " + service);
1623 }
Christopher Tate7060b042014-06-09 19:50:00 -07001624 if (si.applicationInfo.uid != uid) {
1625 throw new IllegalArgumentException("uid " + uid +
1626 " cannot schedule job in " + service.getPackageName());
1627 }
1628 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1629 throw new IllegalArgumentException("Scheduled service " + service
1630 + " does not require android.permission.BIND_JOB_SERVICE permission");
1631 }
Christopher Tate5568f542014-06-18 13:53:31 -07001632 } catch (RemoteException e) {
1633 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001634 }
1635 }
1636
1637 private boolean canPersistJobs(int pid, int uid) {
1638 // If we get this far we're good to go; all we need to do now is check
1639 // whether the app is allowed to persist its scheduled work.
1640 final boolean canPersist;
1641 synchronized (mPersistCache) {
1642 Boolean cached = mPersistCache.get(uid);
1643 if (cached != null) {
1644 canPersist = cached.booleanValue();
1645 } else {
1646 // Persisting jobs is tantamount to running at boot, so we permit
1647 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1648 // permission
1649 int result = getContext().checkPermission(
1650 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1651 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1652 mPersistCache.put(uid, canPersist);
1653 }
1654 }
1655 return canPersist;
1656 }
1657
1658 // IJobScheduler implementation
1659 @Override
1660 public int schedule(JobInfo job) throws RemoteException {
1661 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001662 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001663 }
1664 final int pid = Binder.getCallingPid();
1665 final int uid = Binder.getCallingUid();
1666
1667 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001668 if (job.isPersisted()) {
1669 if (!canPersistJobs(pid, uid)) {
1670 throw new IllegalArgumentException("Error: requested job be persisted without"
1671 + " holding RECEIVE_BOOT_COMPLETED permission.");
1672 }
1673 }
Christopher Tate7060b042014-06-09 19:50:00 -07001674
Jeff Sharkey785f4942016-07-14 10:31:15 -06001675 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1676 getContext().enforceCallingOrSelfPermission(
1677 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1678 }
1679
Christopher Tate7060b042014-06-09 19:50:00 -07001680 long ident = Binder.clearCallingIdentity();
1681 try {
Matthew Williams900c67f2014-07-09 12:46:53 -07001682 return JobSchedulerService.this.schedule(job, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001683 } finally {
1684 Binder.restoreCallingIdentity(ident);
1685 }
1686 }
1687
1688 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001689 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001690 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001691 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001692 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001693 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1694 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001695 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001696
1697 if (packageName == null) {
1698 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001699 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001700
1701 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1702 android.Manifest.permission.UPDATE_DEVICE_STATS);
1703 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1704 throw new SecurityException("Caller uid " + callerUid
1705 + " not permitted to schedule jobs for other apps");
1706 }
1707
Jeff Sharkey4f100402016-05-03 17:44:23 -06001708 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1709 getContext().enforceCallingOrSelfPermission(
1710 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1711 }
1712
Shreyas Basarge968ac752016-01-11 23:09:26 +00001713 long ident = Binder.clearCallingIdentity();
1714 try {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001715 return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001716 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001717 } finally {
1718 Binder.restoreCallingIdentity(ident);
1719 }
1720 }
1721
1722 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001723 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1724 final int uid = Binder.getCallingUid();
1725
1726 long ident = Binder.clearCallingIdentity();
1727 try {
1728 return JobSchedulerService.this.getPendingJobs(uid);
1729 } finally {
1730 Binder.restoreCallingIdentity(ident);
1731 }
1732 }
1733
1734 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001735 public JobInfo getPendingJob(int jobId) throws RemoteException {
1736 final int uid = Binder.getCallingUid();
1737
1738 long ident = Binder.clearCallingIdentity();
1739 try {
1740 return JobSchedulerService.this.getPendingJob(uid, jobId);
1741 } finally {
1742 Binder.restoreCallingIdentity(ident);
1743 }
1744 }
1745
1746 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001747 public void cancelAll() throws RemoteException {
1748 final int uid = Binder.getCallingUid();
1749
1750 long ident = Binder.clearCallingIdentity();
1751 try {
Dianne Hackborne07641d2016-11-09 15:07:23 -08001752 JobSchedulerService.this.cancelJobsForUid(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001753 } finally {
1754 Binder.restoreCallingIdentity(ident);
1755 }
1756 }
1757
1758 @Override
1759 public void cancel(int jobId) throws RemoteException {
1760 final int uid = Binder.getCallingUid();
1761
1762 long ident = Binder.clearCallingIdentity();
1763 try {
1764 JobSchedulerService.this.cancelJob(uid, jobId);
1765 } finally {
1766 Binder.restoreCallingIdentity(ident);
1767 }
1768 }
1769
1770 /**
1771 * "dumpsys" infrastructure
1772 */
1773 @Override
1774 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1775 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1776
1777 long identityToken = Binder.clearCallingIdentity();
1778 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001779 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001780 } finally {
1781 Binder.restoreCallingIdentity(identityToken);
1782 }
1783 }
Christopher Tate5d346052016-03-08 12:56:08 -08001784
1785 @Override
1786 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001787 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08001788 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07001789 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08001790 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001791 };
1792
Christopher Tate5d346052016-03-08 12:56:08 -08001793 // Shell command infrastructure: run the given job immediately
1794 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1795 if (DEBUG) {
1796 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1797 + " " + jobId + " f=" + force);
1798 }
1799
1800 try {
1801 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1802 if (uid < 0) {
1803 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1804 }
1805
1806 synchronized (mLock) {
1807 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1808 if (js == null) {
1809 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1810 }
1811
1812 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1813 if (!js.isConstraintsSatisfied()) {
1814 js.overrideState = 0;
1815 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1816 }
1817
1818 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1819 }
1820 } catch (RemoteException e) {
1821 // can't happen
1822 }
1823 return 0;
1824 }
1825
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001826 void setMonitorBattery(boolean enabled) {
1827 synchronized (mLock) {
1828 if (mBatteryController != null) {
1829 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
1830 }
1831 }
1832 }
1833
1834 int getBatterySeq() {
1835 synchronized (mLock) {
1836 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
1837 }
1838 }
1839
1840 boolean getBatteryCharging() {
1841 synchronized (mLock) {
1842 return mBatteryController != null
1843 ? mBatteryController.getTracker().isOnStablePower() : false;
1844 }
1845 }
1846
1847 boolean getBatteryNotLow() {
1848 synchronized (mLock) {
1849 return mBatteryController != null
1850 ? mBatteryController.getTracker().isBatteryNotLow() : false;
1851 }
1852 }
1853
Dianne Hackborn532ea262017-03-17 17:50:55 -07001854 int getStorageSeq() {
1855 synchronized (mLock) {
1856 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
1857 }
1858 }
1859
1860 boolean getStorageNotLow() {
1861 synchronized (mLock) {
1862 return mStorageController != null
1863 ? mStorageController.getTracker().isStorageNotLow() : false;
1864 }
1865 }
1866
Shreyas Basarge5db09082016-01-07 13:38:29 +00001867 private String printContextIdToJobMap(JobStatus[] map, String initial) {
1868 StringBuilder s = new StringBuilder(initial + ": ");
1869 for (int i=0; i<map.length; i++) {
1870 s.append("(")
1871 .append(map[i] == null? -1: map[i].getJobId())
1872 .append(map[i] == null? -1: map[i].getUid())
1873 .append(")" );
1874 }
1875 return s.toString();
1876 }
1877
1878 private String printPendingQueue() {
1879 StringBuilder s = new StringBuilder("Pending queue: ");
1880 Iterator<JobStatus> it = mPendingJobs.iterator();
1881 while (it.hasNext()) {
1882 JobStatus js = it.next();
1883 s.append("(")
1884 .append(js.getJob().getId())
1885 .append(", ")
1886 .append(js.getUid())
1887 .append(") ");
1888 }
1889 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07001890 }
Christopher Tate7060b042014-06-09 19:50:00 -07001891
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001892 static void dumpHelp(PrintWriter pw) {
1893 pw.println("Job Scheduler (jobscheduler) dump options:");
1894 pw.println(" [-h] [package] ...");
1895 pw.println(" -h: print this help");
1896 pw.println(" [package] is an optional package name to limit the output to.");
1897 }
1898
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001899 void dumpInternal(final PrintWriter pw, String[] args) {
1900 int filterUid = -1;
1901 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001902 int opti = 0;
1903 while (opti < args.length) {
1904 String arg = args[opti];
1905 if ("-h".equals(arg)) {
1906 dumpHelp(pw);
1907 return;
1908 } else if ("-a".equals(arg)) {
1909 // Ignore, we always dump all.
1910 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1911 pw.println("Unknown option: " + arg);
1912 return;
1913 } else {
1914 break;
1915 }
1916 opti++;
1917 }
1918 if (opti < args.length) {
1919 String pkg = args[opti];
1920 try {
1921 filterUid = getContext().getPackageManager().getPackageUid(pkg,
Amith Yamasani0d1fd8d2016-10-12 14:21:51 -07001922 PackageManager.MATCH_ANY_USER);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001923 } catch (NameNotFoundException ignored) {
1924 pw.println("Invalid package: " + pkg);
1925 return;
1926 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001927 }
1928 }
1929
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001930 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07001931 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001932 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001933 mConstants.dump(pw);
1934 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001935 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001936 pw.print("Registered ");
1937 pw.print(mJobs.size());
1938 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07001939 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001940 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1941 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001942 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001943 public int compare(JobStatus o1, JobStatus o2) {
1944 int uid1 = o1.getUid();
1945 int uid2 = o2.getUid();
1946 int id1 = o1.getJobId();
1947 int id2 = o2.getJobId();
1948 if (uid1 != uid2) {
1949 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001950 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001951 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001952 }
1953 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001954 for (JobStatus job : jobs) {
1955 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
1956 pw.println(job.toShortStringExceptUniqueId());
1957
1958 // Skip printing details if the caller requested a filter
1959 if (!job.shouldDump(filterUidFinal)) {
1960 continue;
1961 }
1962
1963 job.dump(pw, " ", true);
1964 pw.print(" Ready: ");
1965 pw.print(mHandler.isReadyToBeExecutedLocked(job));
1966 pw.print(" (job=");
1967 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001968 pw.print(" user=");
1969 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07001970 pw.print(" !pending=");
1971 pw.print(!mPendingJobs.contains(job));
1972 pw.print(" !active=");
1973 pw.print(!isCurrentlyActiveLocked(job));
1974 pw.print(" comp=");
1975 boolean componentPresent = false;
1976 try {
1977 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1978 job.getServiceComponent(),
1979 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1980 job.getUserId()) != null);
1981 } catch (RemoteException e) {
1982 }
1983 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001984 pw.println(")");
1985 }
Christopher Tate7060b042014-06-09 19:50:00 -07001986 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07001987 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07001988 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001989 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07001990 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001991 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07001992 }
1993 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001994 pw.println("Uid priority overrides:");
1995 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001996 int uid = mUidPriorityOverride.keyAt(i);
1997 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
1998 pw.print(" "); pw.print(UserHandle.formatUid(uid));
1999 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2000 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002001 }
2002 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002003 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002004 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002005 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2006 pw.println();
2007 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002008 pw.println("Pending queue:");
2009 for (int i=0; i<mPendingJobs.size(); i++) {
2010 JobStatus job = mPendingJobs.get(i);
2011 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2012 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002013 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002014 int priority = evaluateJobPriorityLocked(job);
2015 if (priority != JobInfo.PRIORITY_DEFAULT) {
2016 pw.print(" Evaluated priority: "); pw.println(priority);
2017 }
2018 pw.print(" Tag: "); pw.println(job.getTag());
2019 }
Christopher Tate7060b042014-06-09 19:50:00 -07002020 pw.println();
2021 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002022 for (int i=0; i<mActiveServices.size(); i++) {
2023 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002024 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Shreyas Basarge5db09082016-01-07 13:38:29 +00002025 if (jsc.getRunningJob() == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002026 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07002027 continue;
2028 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002029 pw.println(jsc.getRunningJob().toShortString());
2030 pw.print(" Running for: ");
2031 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
2032 pw.print(", timeout at: ");
2033 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
2034 pw.println();
2035 jsc.getRunningJob().dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002036 int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
2037 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002038 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002039 }
Christopher Tate7060b042014-06-09 19:50:00 -07002040 }
2041 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002042 if (filterUid == -1) {
2043 pw.println();
2044 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2045 pw.print("mReportedActive="); pw.println(mReportedActive);
2046 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2047 }
Christopher Tate7060b042014-06-09 19:50:00 -07002048 }
2049 pw.println();
2050 }
2051}