blob: 2de9aaedf7c800280577c6ba21bfd76809112c58 [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 Hackborna47223f2017-03-30 13:49:13 -0700619 // This may throw a SecurityException.
620 jobStatus.prepare(ActivityManager.getService());
621
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800622 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700623 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700624 cancelJobImpl(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700625 }
626 startTrackingJob(jobStatus, toCancel);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800627 }
Matthew Williamsbafeeb92014-08-08 11:51:06 -0700628 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700629 return JobScheduler.RESULT_SUCCESS;
630 }
631
632 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800633 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800634 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
635 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
636 for (int i = jobs.size() - 1; i >= 0; i--) {
637 JobStatus job = jobs.get(i);
638 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700639 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800640 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700641 }
Christopher Tate7060b042014-06-09 19:50:00 -0700642 }
643
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600644 public JobInfo getPendingJob(int uid, int jobId) {
645 synchronized (mLock) {
646 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
647 for (int i = jobs.size() - 1; i >= 0; i--) {
648 JobStatus job = jobs.get(i);
649 if (job.getJobId() == jobId) {
650 return job.getJob();
651 }
652 }
653 return null;
654 }
655 }
656
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700657 void cancelJobsForUser(int userHandle) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700658 List<JobStatus> jobsForUser;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800659 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700660 jobsForUser = mJobs.getJobsByUser(userHandle);
661 }
662 for (int i=0; i<jobsForUser.size(); i++) {
663 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700664 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700665 }
666 }
667
Christopher Tateee7805b2016-07-15 16:56:56 -0700668 void cancelJobsForPackageAndUid(String pkgName, int uid) {
669 List<JobStatus> jobsForUid;
670 synchronized (mLock) {
671 jobsForUid = mJobs.getJobsByUid(uid);
672 }
673 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
674 final JobStatus job = jobsForUid.get(i);
675 if (job.getSourcePackageName().equals(pkgName)) {
676 cancelJobImpl(job, null);
677 }
678 }
679 }
680
Christopher Tate7060b042014-06-09 19:50:00 -0700681 /**
682 * Entry point from client to cancel all jobs originating from their uid.
683 * This will remove the job from the master list, and cancel the job if it was staged for
684 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700685 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800686 *
Christopher Tate7060b042014-06-09 19:50:00 -0700687 */
Dianne Hackborne07641d2016-11-09 15:07:23 -0800688 public void cancelJobsForUid(int uid) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700689 List<JobStatus> jobsForUid;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800690 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700691 jobsForUid = mJobs.getJobsByUid(uid);
692 }
693 for (int i=0; i<jobsForUid.size(); i++) {
694 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700695 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700696 }
697 }
698
699 /**
700 * Entry point from client to cancel the job corresponding to the jobId provided.
701 * This will remove the job from the master list, and cancel the job if it was staged for
702 * execution or being executed.
703 * @param uid Uid of the calling client.
704 * @param jobId Id of the job, provided at schedule-time.
705 */
706 public void cancelJob(int uid, int jobId) {
707 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800708 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700709 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Matthew Williams48a30db2014-09-23 13:39:36 -0700710 }
711 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700712 cancelJobImpl(toCancel, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700713 }
714 }
715
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700716 private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800717 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700718 cancelled.unprepare(ActivityManager.getService());
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700719 stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800720 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700721 // Remove from pending queue.
Dianne Hackborn807de782016-04-07 17:54:41 -0700722 if (mPendingJobs.remove(cancelled)) {
723 mJobPackageTracker.noteNonpending(cancelled);
724 }
Matthew Williams48a30db2014-09-23 13:39:36 -0700725 // Cancel if running.
Shreyas Basarge5db09082016-01-07 13:38:29 +0000726 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800727 reportActive();
Matthew Williams48a30db2014-09-23 13:39:36 -0700728 }
Christopher Tate7060b042014-06-09 19:50:00 -0700729 }
730
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800731 void updateUidState(int uid, int procState) {
732 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800733 if (procState == ActivityManager.PROCESS_STATE_TOP) {
734 // Only use this if we are exactly the top app. All others can live
735 // with just the foreground priority. This means that persistent processes
736 // can never be the top app priority... that is fine.
737 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
738 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
739 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800740 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800741 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800742 }
743 }
744 }
745
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700746 @Override
747 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800748 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700749 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -0600750 // When becoming idle, make sure no jobs are actively running,
751 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700752 for (int i=0; i<mActiveServices.size(); i++) {
753 JobServiceContext jsc = mActiveServices.get(i);
754 final JobStatus executing = jsc.getRunningJob();
Jeff Sharkey34618b52016-06-01 15:51:19 -0600755 if (executing != null
756 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700757 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
758 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700759 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700760 } else {
761 // When coming out of idle, allow thing to start back up.
762 if (mReadyToRock) {
763 if (mLocalDeviceIdleController != null) {
764 if (!mReportedActive) {
765 mReportedActive = true;
766 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700767 }
768 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700769 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700770 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700771 }
772 }
773 }
774
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800775 void reportActive() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000776 // active is true if pending queue contains jobs OR some job is running.
777 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800778 if (mPendingJobs.size() <= 0) {
779 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700780 final JobServiceContext jsc = mActiveServices.get(i);
781 final JobStatus job = jsc.getRunningJob();
782 if (job != null
783 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
784 && !job.dozeWhitelisted) {
785 // We will report active if we have a job running and it is not an exception
786 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800787 active = true;
788 break;
789 }
790 }
791 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000792
793 if (mReportedActive != active) {
794 mReportedActive = active;
795 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800796 mLocalDeviceIdleController.setJobsActive(active);
797 }
798 }
799 }
800
Christopher Tate7060b042014-06-09 19:50:00 -0700801 /**
802 * Initializes the system service.
803 * <p>
804 * Subclasses must define a single argument constructor that accepts the context
805 * and passes it to super.
806 * </p>
807 *
808 * @param context The system server context.
809 */
810 public JobSchedulerService(Context context) {
811 super(context);
Dianne Hackborn970e3f42016-06-01 10:55:13 -0700812 mHandler = new JobHandler(context.getMainLooper());
813 mConstants = new Constants(mHandler);
814 mJobSchedulerStub = new JobSchedulerStub();
815 mJobs = JobStore.initAndGet(this);
816
Christopher Tate7060b042014-06-09 19:50:00 -0700817 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700818 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700819 mControllers.add(ConnectivityController.get(this));
820 mControllers.add(TimeController.get(this));
821 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800822 mBatteryController = BatteryController.get(this);
823 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700824 mStorageController = StorageController.get(this);
825 mControllers.add(mStorageController);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800826 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800827 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700828 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700829 }
830
831 @Override
832 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000833 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700834 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
835 }
836
837 @Override
838 public void onBootPhase(int phase) {
839 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700840 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000841 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700842 final IntentFilter filter = new IntentFilter();
843 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
844 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -0700845 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
846 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -0700847 filter.addDataScheme("package");
848 getContext().registerReceiverAsUser(
849 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
850 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
851 getContext().registerReceiverAsUser(
852 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000853 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700854 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800855 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800856 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800857 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
858 null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700859 } catch (RemoteException e) {
860 // ignored; both services live in system_server
861 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700862 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800863 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700864 // Let's go!
865 mReadyToRock = true;
866 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
867 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800868 mLocalDeviceIdleController
869 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700870 // Create the "runners".
871 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
872 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700873 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700874 getContext().getMainLooper()));
875 }
876 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800877 mJobs.forEachJob(new JobStatusFunctor() {
878 @Override
879 public void process(JobStatus job) {
880 for (int controller = 0; controller < mControllers.size(); controller++) {
881 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800882 sc.maybeStartTrackingJobLocked(job, null);
883 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700884 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800885 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700886 // GO GO GO!
887 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
888 }
Christopher Tate7060b042014-06-09 19:50:00 -0700889 }
890 }
891
892 /**
893 * Called when we have a job status object that we need to insert in our
894 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
895 * about.
896 */
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800897 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800898 synchronized (mLock) {
Dianne Hackborna47223f2017-03-30 13:49:13 -0700899 if (!jobStatus.isPrepared()) {
900 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
901 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800902 final boolean update = mJobs.add(jobStatus);
903 if (mReadyToRock) {
904 for (int i = 0; i < mControllers.size(); i++) {
905 StateController controller = mControllers.get(i);
906 if (update) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700907 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800908 }
909 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700910 }
Christopher Tate7060b042014-06-09 19:50:00 -0700911 }
Christopher Tate7060b042014-06-09 19:50:00 -0700912 }
913 }
914
915 /**
916 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
917 * object removed.
918 */
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700919 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
920 boolean writeBack) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800921 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700922 // Remove from store as well as controllers.
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800923 final boolean removed = mJobs.remove(jobStatus, writeBack);
924 if (removed && mReadyToRock) {
925 for (int i=0; i<mControllers.size(); i++) {
926 StateController controller = mControllers.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700927 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800928 }
Christopher Tate7060b042014-06-09 19:50:00 -0700929 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800930 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -0700931 }
Christopher Tate7060b042014-06-09 19:50:00 -0700932 }
933
Shreyas Basarge5db09082016-01-07 13:38:29 +0000934 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700935 for (int i=0; i<mActiveServices.size(); i++) {
936 JobServiceContext jsc = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700937 final JobStatus executing = jsc.getRunningJob();
938 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Shreyas Basarge5db09082016-01-07 13:38:29 +0000939 jsc.cancelExecutingJob(reason);
Christopher Tate7060b042014-06-09 19:50:00 -0700940 return true;
941 }
942 }
943 return false;
944 }
945
946 /**
947 * @param job JobStatus we are querying against.
948 * @return Whether or not the job represented by the status object is currently being run or
949 * is pending.
950 */
951 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700952 for (int i=0; i<mActiveServices.size(); i++) {
953 JobServiceContext serviceContext = mActiveServices.get(i);
Christopher Tateeafb5352016-10-04 16:34:48 -0700954 // The 'unsafe' direct-internal-reference running-job inspector is okay to
955 // use here because we are already holding the necessary lock *and* we
956 // immediately discard the returned object reference, if any; we return
957 // only a boolean state indicator to the caller.
958 final JobStatus running = serviceContext.getRunningJobUnsafeLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700959 if (running != null && running.matches(job.getUid(), job.getJobId())) {
960 return true;
961 }
962 }
963 return false;
964 }
965
Dianne Hackborn807de782016-04-07 17:54:41 -0700966 void noteJobsPending(List<JobStatus> jobs) {
967 for (int i = jobs.size() - 1; i >= 0; i--) {
968 JobStatus job = jobs.get(i);
969 mJobPackageTracker.notePending(job);
970 }
971 }
972
973 void noteJobsNonpending(List<JobStatus> jobs) {
974 for (int i = jobs.size() - 1; i >= 0; i--) {
975 JobStatus job = jobs.get(i);
976 mJobPackageTracker.noteNonpending(job);
977 }
978 }
979
Christopher Tate7060b042014-06-09 19:50:00 -0700980 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700981 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
982 * specify an override deadline on a failed job (the failed job will run even though it's not
983 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
984 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
985 *
Christopher Tate7060b042014-06-09 19:50:00 -0700986 * @param failureToReschedule Provided job status that we will reschedule.
987 * @return A newly instantiated JobStatus with the same constraints as the last job except
988 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700989 *
990 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
Christopher Tate7060b042014-06-09 19:50:00 -0700991 */
992 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
993 final long elapsedNowMillis = SystemClock.elapsedRealtime();
994 final JobInfo job = failureToReschedule.getJob();
995
996 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -0700997 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
998 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -0700999
1000 switch (job.getBackoffPolicy()) {
Matthew Williamsd1c06752014-08-22 14:15:28 -07001001 case JobInfo.BACKOFF_POLICY_LINEAR:
1002 delayMillis = initialBackoffMillis * backoffAttempts;
Christopher Tate7060b042014-06-09 19:50:00 -07001003 break;
1004 default:
1005 if (DEBUG) {
1006 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1007 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001008 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
1009 delayMillis =
1010 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
Christopher Tate7060b042014-06-09 19:50:00 -07001011 break;
1012 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001013 delayMillis =
1014 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001015 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -07001016 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001017 for (int ic=0; ic<mControllers.size(); ic++) {
1018 StateController controller = mControllers.get(ic);
1019 controller.rescheduleForFailure(newJob, failureToReschedule);
1020 }
1021 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001022 }
1023
1024 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001025 * Called after a periodic has executed so we can reschedule it. We take the last execution
1026 * time of the job to be the time of completion (i.e. the time at which this function is
1027 * called).
Christopher Tate7060b042014-06-09 19:50:00 -07001028 * This could be inaccurate b/c the job can run for as long as
1029 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1030 * to underscheduling at least, rather than if we had taken the last execution time to be the
1031 * start of the execution.
1032 * @return A new job representing the execution criteria for this instantiation of the
1033 * recurring job.
1034 */
1035 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1036 final long elapsedNow = SystemClock.elapsedRealtime();
1037 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001038 long runEarly = 0L;
1039
1040 // If this periodic was rescheduled it won't have a deadline.
1041 if (periodicToReschedule.hasDeadlineConstraint()) {
1042 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1043 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001044 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001045 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001046 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1047 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001048
1049 if (DEBUG) {
1050 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1051 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1052 }
1053 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1054 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1055 }
1056
1057 // JobCompletedListener implementations.
1058
1059 /**
1060 * A job just finished executing. We fetch the
1061 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1062 * whether we want to reschedule we readd it to the controllers.
1063 * @param jobStatus Completed job.
1064 * @param needsReschedule Whether the implementing class should reschedule this job.
1065 */
1066 @Override
1067 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
1068 if (DEBUG) {
1069 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1070 }
Shreyas Basarge73f10252016-02-11 17:06:13 +00001071 // Do not write back immediately if this is a periodic job. The job may get lost if system
1072 // shuts down before it is added back.
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001073 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001074 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001075 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001076 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001077 // We still want to check for jobs to execute, because this job may have
1078 // scheduled a new job under the same job id, and now we can run it.
1079 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001080 return;
1081 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001082 // Note: there is a small window of time in here where, when rescheduling a job,
1083 // we will stop monitoring its content providers. This should be fixed by stopping
1084 // the old job after scheduling the new one, but since we have no lock held here
1085 // that may cause ordering problems if the app removes jobStatus while in here.
Christopher Tate7060b042014-06-09 19:50:00 -07001086 if (needsReschedule) {
1087 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001088 try {
1089 rescheduled.prepare(ActivityManager.getService());
1090 } catch (SecurityException e) {
1091 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduled);
1092 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001093 startTrackingJob(rescheduled, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001094 } else if (jobStatus.getJob().isPeriodic()) {
1095 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001096 try {
1097 rescheduledPeriodic.prepare(ActivityManager.getService());
1098 } catch (SecurityException e) {
1099 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1100 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001101 startTrackingJob(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001102 }
Dianne Hackborna47223f2017-03-30 13:49:13 -07001103 jobStatus.unprepare(ActivityManager.getService());
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001104 reportActive();
1105 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001106 }
1107
1108 // StateChangedListener implementations.
1109
1110 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001111 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1112 * some controller's state has changed, so as to run through the list of jobs and start/stop
1113 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001114 */
1115 @Override
1116 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001117 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001118 }
1119
1120 @Override
1121 public void onRunJobNow(JobStatus jobStatus) {
1122 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1123 }
1124
Christopher Tate7060b042014-06-09 19:50:00 -07001125 private class JobHandler extends Handler {
1126
1127 public JobHandler(Looper looper) {
1128 super(looper);
1129 }
1130
1131 @Override
1132 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001133 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001134 if (!mReadyToRock) {
1135 return;
1136 }
1137 }
Christopher Tate7060b042014-06-09 19:50:00 -07001138 switch (message.what) {
1139 case MSG_JOB_EXPIRED:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001140 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001141 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001142 // runNow can be null, which is a controller's way of indicating that its
1143 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001144 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001145 mJobPackageTracker.notePending(runNow);
Christopher Tate7060b042014-06-09 19:50:00 -07001146 mPendingJobs.add(runNow);
1147 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001148 queueReadyJobsForExecutionLockedH();
Christopher Tate7060b042014-06-09 19:50:00 -07001149 }
Christopher Tate7060b042014-06-09 19:50:00 -07001150 break;
1151 case MSG_CHECK_JOB:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001152 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001153 if (mReportedActive) {
1154 // if jobs are currently being run, queue all ready jobs for execution.
1155 queueReadyJobsForExecutionLockedH();
1156 } else {
1157 // Check the list of jobs and run some of them if we feel inclined.
1158 maybeQueueReadyJobsForExecutionLockedH();
1159 }
1160 }
1161 break;
1162 case MSG_CHECK_JOB_GREEDY:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001163 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001164 queueReadyJobsForExecutionLockedH();
Matthew Williams48a30db2014-09-23 13:39:36 -07001165 }
Christopher Tate7060b042014-06-09 19:50:00 -07001166 break;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001167 case MSG_STOP_JOB:
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001168 cancelJobImpl((JobStatus)message.obj, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001169 break;
Christopher Tate7060b042014-06-09 19:50:00 -07001170 }
1171 maybeRunPendingJobsH();
1172 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1173 removeMessages(MSG_CHECK_JOB);
1174 }
1175
1176 /**
1177 * Run through list of jobs and execute all possible - at least one is expired so we do
1178 * as many as we can.
1179 */
Matthew Williams48a30db2014-09-23 13:39:36 -07001180 private void queueReadyJobsForExecutionLockedH() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001181 if (DEBUG) {
1182 Slog.d(TAG, "queuing all ready jobs for execution:");
1183 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001184 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001185 mPendingJobs.clear();
1186 mJobs.forEachJob(mReadyQueueFunctor);
1187 mReadyQueueFunctor.postProcess();
1188
Matthew Williams48a30db2014-09-23 13:39:36 -07001189 if (DEBUG) {
1190 final int queuedJobs = mPendingJobs.size();
1191 if (queuedJobs == 0) {
1192 Slog.d(TAG, "No jobs pending.");
1193 } else {
1194 Slog.d(TAG, queuedJobs + " jobs queued.");
Matthew Williams75fc5252014-09-02 16:17:53 -07001195 }
Christopher Tate7060b042014-06-09 19:50:00 -07001196 }
1197 }
1198
Christopher Tate2f36fd62016-02-18 18:36:08 -08001199 class ReadyJobQueueFunctor implements JobStatusFunctor {
1200 ArrayList<JobStatus> newReadyJobs;
1201
1202 @Override
1203 public void process(JobStatus job) {
1204 if (isReadyToBeExecutedLocked(job)) {
1205 if (DEBUG) {
1206 Slog.d(TAG, " queued " + job.toShortString());
1207 }
1208 if (newReadyJobs == null) {
1209 newReadyJobs = new ArrayList<JobStatus>();
1210 }
1211 newReadyJobs.add(job);
1212 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1213 stopJobOnServiceContextLocked(job,
1214 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1215 }
1216 }
1217
1218 public void postProcess() {
1219 if (newReadyJobs != null) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001220 noteJobsPending(newReadyJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001221 mPendingJobs.addAll(newReadyJobs);
1222 }
1223 newReadyJobs = null;
1224 }
1225 }
1226 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1227
Christopher Tate7060b042014-06-09 19:50:00 -07001228 /**
1229 * The state of at least one job has changed. Here is where we could enforce various
1230 * policies on when we want to execute jobs.
1231 * Right now the policy is such:
1232 * If >1 of the ready jobs is idle mode we send all of them off
1233 * if more than 2 network connectivity jobs are ready we send them all off.
1234 * If more than 4 jobs total are ready we send them all off.
1235 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1236 */
Christopher Tate2f36fd62016-02-18 18:36:08 -08001237 class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1238 int chargingCount;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001239 int batteryNotLowCount;
Dianne Hackborn532ea262017-03-17 17:50:55 -07001240 int storageNotLowCount;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001241 int idleCount;
1242 int backoffCount;
1243 int connectivityCount;
1244 int contentCount;
1245 List<JobStatus> runnableJobs;
1246
1247 public MaybeReadyJobQueueFunctor() {
1248 reset();
1249 }
1250
1251 // Functor method invoked for each job via JobStore.forEachJob()
1252 @Override
1253 public void process(JobStatus job) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001254 if (isReadyToBeExecutedLocked(job)) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001255 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001256 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1257 job.getJob().getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001258 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1259 + job.getJob().toString() + " -- package not allowed to start");
1260 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001261 return;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001262 }
1263 } catch (RemoteException e) {
1264 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001265 if (job.getNumFailures() > 0) {
1266 backoffCount++;
Christopher Tate7060b042014-06-09 19:50:00 -07001267 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001268 if (job.hasIdleConstraint()) {
1269 idleCount++;
1270 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001271 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1272 || job.hasNotRoamingConstraint()) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001273 connectivityCount++;
1274 }
1275 if (job.hasChargingConstraint()) {
1276 chargingCount++;
1277 }
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001278 if (job.hasBatteryNotLowConstraint()) {
1279 batteryNotLowCount++;
1280 }
Dianne Hackborn532ea262017-03-17 17:50:55 -07001281 if (job.hasStorageNotLowConstraint()) {
1282 storageNotLowCount++;
1283 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001284 if (job.hasContentTriggerConstraint()) {
1285 contentCount++;
1286 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001287 if (runnableJobs == null) {
1288 runnableJobs = new ArrayList<>();
1289 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001290 runnableJobs.add(job);
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001291 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001292 stopJobOnServiceContextLocked(job,
1293 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001294 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001295 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001296
1297 public void postProcess() {
1298 if (backoffCount > 0 ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001299 idleCount >= mConstants.MIN_IDLE_COUNT ||
1300 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1301 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001302 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
Dianne Hackborn532ea262017-03-17 17:50:55 -07001303 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001304 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1305 (runnableJobs != null
1306 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001307 if (DEBUG) {
1308 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1309 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001310 noteJobsPending(runnableJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001311 mPendingJobs.addAll(runnableJobs);
1312 } else {
1313 if (DEBUG) {
1314 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1315 }
Christopher Tate7060b042014-06-09 19:50:00 -07001316 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001317
1318 // Be ready for next time
1319 reset();
Matthew Williams48a30db2014-09-23 13:39:36 -07001320 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001321
1322 private void reset() {
1323 chargingCount = 0;
1324 idleCount = 0;
1325 backoffCount = 0;
1326 connectivityCount = 0;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001327 batteryNotLowCount = 0;
Dianne Hackborn532ea262017-03-17 17:50:55 -07001328 storageNotLowCount = 0;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001329 contentCount = 0;
1330 runnableJobs = null;
1331 }
1332 }
1333 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1334
1335 private void maybeQueueReadyJobsForExecutionLockedH() {
1336 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1337
Dianne Hackborn807de782016-04-07 17:54:41 -07001338 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001339 mPendingJobs.clear();
1340 mJobs.forEachJob(mMaybeQueueFunctor);
1341 mMaybeQueueFunctor.postProcess();
Christopher Tate7060b042014-06-09 19:50:00 -07001342 }
1343
1344 /**
1345 * Criteria for moving a job into the pending queue:
1346 * - It's ready.
1347 * - It's not pending.
1348 * - It's not already running on a JSC.
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001349 * - The user that requested the job is running.
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001350 * - The component is enabled and runnable.
Christopher Tate7060b042014-06-09 19:50:00 -07001351 */
1352 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Christopher Tateb1d64482017-03-15 12:01:22 -07001353 final boolean jobExists = mJobs.containsJob(job);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001354 final boolean jobReady = job.isReady();
1355 final boolean jobPending = mPendingJobs.contains(job);
1356 final boolean jobActive = isCurrentlyActiveLocked(job);
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001357
1358 final int userId = job.getUserId();
1359 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
Christopher Tateb1d64482017-03-15 12:01:22 -07001360
1361 if (DEBUG) {
1362 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1363 + " exists=" + jobExists
1364 + " ready=" + jobReady + " pending=" + jobPending
1365 + " active=" + jobActive + " userStarted=" + userStarted);
1366 }
1367
1368 // Short circuit: don't do the expensive PM check unless we really think
1369 // we might need to run this job now.
1370 if (!jobExists || !userStarted || !jobReady || jobPending || jobActive) {
1371 return false;
1372 }
1373
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001374 final boolean componentPresent;
1375 try {
1376 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1377 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1378 userId) != null);
1379 } catch (RemoteException e) {
1380 throw e.rethrowAsRuntimeException();
1381 }
1382
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001383 if (DEBUG) {
1384 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001385 + " componentPresent=" + componentPresent);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001386 }
Christopher Tateb1d64482017-03-15 12:01:22 -07001387
1388 // Everything else checked out so far, so this is the final yes/no check
1389 return componentPresent;
Christopher Tate7060b042014-06-09 19:50:00 -07001390 }
1391
1392 /**
1393 * Criteria for cancelling an active job:
1394 * - It's not ready
1395 * - It's running on a JSC.
1396 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001397 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
Christopher Tate7060b042014-06-09 19:50:00 -07001398 return !job.isReady() && isCurrentlyActiveLocked(job);
1399 }
1400
1401 /**
1402 * Reconcile jobs in the pending queue against available execution contexts.
1403 * A controller can force a job into the pending queue even if it's already running, but
1404 * here is where we decide whether to actually execute it.
1405 */
1406 private void maybeRunPendingJobsH() {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001407 synchronized (mLock) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001408 if (DEBUG) {
1409 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1410 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001411 assignJobsToContextsLocked();
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001412 reportActive();
Christopher Tate7060b042014-06-09 19:50:00 -07001413 }
1414 }
1415 }
1416
Dianne Hackborn807de782016-04-07 17:54:41 -07001417 private int adjustJobPriority(int curPriority, JobStatus job) {
1418 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1419 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001420 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001421 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001422 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001423 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1424 }
1425 }
1426 return curPriority;
1427 }
1428
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001429 private int evaluateJobPriorityLocked(JobStatus job) {
1430 int priority = job.getPriority();
1431 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001432 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001433 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001434 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1435 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001436 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001437 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001438 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001439 }
1440
Christopher Tate7060b042014-06-09 19:50:00 -07001441 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001442 * Takes jobs from pending queue and runs them on available contexts.
1443 * If no contexts are available, preempts lower priority jobs to
1444 * run higher priority ones.
1445 * Lock on mJobs before calling this function.
1446 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001447 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001448 if (DEBUG) {
1449 Slog.d(TAG, printPendingQueue());
1450 }
1451
Dianne Hackborn970510b2016-02-24 16:56:42 -08001452 int memLevel;
1453 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001454 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001455 } catch (RemoteException e) {
1456 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1457 }
1458 switch (memLevel) {
1459 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001460 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001461 break;
1462 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001463 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001464 break;
1465 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001466 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001467 break;
1468 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001469 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001470 break;
1471 }
1472
1473 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1474 boolean[] act = mTmpAssignAct;
1475 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1476 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001477 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001478 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1479 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001480 final JobStatus status = js.getRunningJob();
1481 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001482 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001483 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1484 numForeground++;
1485 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001486 }
1487 act[i] = false;
1488 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001489 }
1490 if (DEBUG) {
1491 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1492 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001493 for (int i=0; i<mPendingJobs.size(); i++) {
1494 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001495
1496 // If job is already running, go to next job.
1497 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1498 if (jobRunningContext != -1) {
1499 continue;
1500 }
1501
Dianne Hackborn970510b2016-02-24 16:56:42 -08001502 final int priority = evaluateJobPriorityLocked(nextPending);
1503 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001504
Shreyas Basarge5db09082016-01-07 13:38:29 +00001505 // Find a context for nextPending. The context should be available OR
1506 // it should have lowest priority among all running jobs
1507 // (sharing the same Uid as nextPending)
1508 int minPriority = Integer.MAX_VALUE;
1509 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001510 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1511 JobStatus job = contextIdToJobMap[j];
1512 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001513 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001514 if ((numActive < mMaxActiveJobs ||
1515 (priority >= JobInfo.PRIORITY_TOP_APP &&
1516 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001517 (preferredUid == nextPending.getUid() ||
1518 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1519 // This slot is free, and we haven't yet hit the limit on
1520 // concurrent jobs... we can just throw the job in to here.
1521 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001522 break;
1523 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001524 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001525 // the context has a preferred Uid or we have reached the limit on
1526 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001527 continue;
1528 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001529 if (job.getUid() != nextPending.getUid()) {
1530 continue;
1531 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001532 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001533 continue;
1534 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001535 if (minPriority > nextPending.lastEvaluatedPriority) {
1536 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001537 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001538 }
1539 }
1540 if (minPriorityContextId != -1) {
1541 contextIdToJobMap[minPriorityContextId] = nextPending;
1542 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001543 numActive++;
1544 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1545 numForeground++;
1546 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001547 }
1548 }
1549 if (DEBUG) {
1550 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1551 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001552 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001553 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001554 boolean preservePreferredUid = false;
1555 if (act[i]) {
1556 JobStatus js = mActiveServices.get(i).getRunningJob();
1557 if (js != null) {
1558 if (DEBUG) {
1559 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1560 }
1561 // preferredUid will be set to uid of currently running job.
1562 mActiveServices.get(i).preemptExecutingJob();
1563 preservePreferredUid = true;
1564 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001565 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001566 if (DEBUG) {
1567 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001568 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001569 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001570 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001571 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001572 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001573 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1574 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001575 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001576 if (mPendingJobs.remove(pendingJob)) {
1577 mJobPackageTracker.noteNonpending(pendingJob);
1578 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001579 }
1580 }
1581 if (!preservePreferredUid) {
1582 mActiveServices.get(i).clearPreferredUid();
1583 }
1584 }
1585 }
1586
1587 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1588 for (int i=0; i<map.length; i++) {
1589 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1590 return i;
1591 }
1592 }
1593 return -1;
1594 }
1595
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001596 final class LocalService implements JobSchedulerInternal {
1597
1598 /**
1599 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1600 * jobs are always considered pending.
1601 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001602 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001603 public List<JobInfo> getSystemScheduledPendingJobs() {
1604 synchronized (mLock) {
1605 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1606 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1607 @Override
1608 public void process(JobStatus job) {
1609 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1610 pendingJobs.add(job.getJob());
1611 }
1612 }
1613 });
1614 return pendingJobs;
1615 }
1616 }
1617 }
1618
Shreyas Basarge5db09082016-01-07 13:38:29 +00001619 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001620 * Binder stub trampoline implementation
1621 */
1622 final class JobSchedulerStub extends IJobScheduler.Stub {
1623 /** Cache determination of whether a given app can persist jobs
1624 * key is uid of the calling app; value is undetermined/true/false
1625 */
1626 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1627
1628 // Enforce that only the app itself (or shared uid participant) can schedule a
1629 // job that runs one of the app's services, as well as verifying that the
1630 // named service properly requires the BIND_JOB_SERVICE permission
1631 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001632 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001633 final ComponentName service = job.getService();
1634 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001635 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001636 PackageManager.MATCH_DIRECT_BOOT_AWARE
1637 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001638 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001639 if (si == null) {
1640 throw new IllegalArgumentException("No such service " + service);
1641 }
Christopher Tate7060b042014-06-09 19:50:00 -07001642 if (si.applicationInfo.uid != uid) {
1643 throw new IllegalArgumentException("uid " + uid +
1644 " cannot schedule job in " + service.getPackageName());
1645 }
1646 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1647 throw new IllegalArgumentException("Scheduled service " + service
1648 + " does not require android.permission.BIND_JOB_SERVICE permission");
1649 }
Christopher Tate5568f542014-06-18 13:53:31 -07001650 } catch (RemoteException e) {
1651 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001652 }
1653 }
1654
1655 private boolean canPersistJobs(int pid, int uid) {
1656 // If we get this far we're good to go; all we need to do now is check
1657 // whether the app is allowed to persist its scheduled work.
1658 final boolean canPersist;
1659 synchronized (mPersistCache) {
1660 Boolean cached = mPersistCache.get(uid);
1661 if (cached != null) {
1662 canPersist = cached.booleanValue();
1663 } else {
1664 // Persisting jobs is tantamount to running at boot, so we permit
1665 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1666 // permission
1667 int result = getContext().checkPermission(
1668 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1669 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1670 mPersistCache.put(uid, canPersist);
1671 }
1672 }
1673 return canPersist;
1674 }
1675
1676 // IJobScheduler implementation
1677 @Override
1678 public int schedule(JobInfo job) throws RemoteException {
1679 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001680 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001681 }
1682 final int pid = Binder.getCallingPid();
1683 final int uid = Binder.getCallingUid();
1684
1685 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001686 if (job.isPersisted()) {
1687 if (!canPersistJobs(pid, uid)) {
1688 throw new IllegalArgumentException("Error: requested job be persisted without"
1689 + " holding RECEIVE_BOOT_COMPLETED permission.");
1690 }
1691 }
Christopher Tate7060b042014-06-09 19:50:00 -07001692
Jeff Sharkey785f4942016-07-14 10:31:15 -06001693 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1694 getContext().enforceCallingOrSelfPermission(
1695 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1696 }
1697
Christopher Tate7060b042014-06-09 19:50:00 -07001698 long ident = Binder.clearCallingIdentity();
1699 try {
Matthew Williams900c67f2014-07-09 12:46:53 -07001700 return JobSchedulerService.this.schedule(job, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001701 } finally {
1702 Binder.restoreCallingIdentity(ident);
1703 }
1704 }
1705
1706 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001707 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001708 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001709 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001710 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001711 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1712 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001713 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001714
1715 if (packageName == null) {
1716 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001717 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001718
1719 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1720 android.Manifest.permission.UPDATE_DEVICE_STATS);
1721 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1722 throw new SecurityException("Caller uid " + callerUid
1723 + " not permitted to schedule jobs for other apps");
1724 }
1725
Jeff Sharkey4f100402016-05-03 17:44:23 -06001726 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1727 getContext().enforceCallingOrSelfPermission(
1728 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1729 }
1730
Shreyas Basarge968ac752016-01-11 23:09:26 +00001731 long ident = Binder.clearCallingIdentity();
1732 try {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001733 return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001734 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001735 } finally {
1736 Binder.restoreCallingIdentity(ident);
1737 }
1738 }
1739
1740 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001741 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1742 final int uid = Binder.getCallingUid();
1743
1744 long ident = Binder.clearCallingIdentity();
1745 try {
1746 return JobSchedulerService.this.getPendingJobs(uid);
1747 } finally {
1748 Binder.restoreCallingIdentity(ident);
1749 }
1750 }
1751
1752 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001753 public JobInfo getPendingJob(int jobId) throws RemoteException {
1754 final int uid = Binder.getCallingUid();
1755
1756 long ident = Binder.clearCallingIdentity();
1757 try {
1758 return JobSchedulerService.this.getPendingJob(uid, jobId);
1759 } finally {
1760 Binder.restoreCallingIdentity(ident);
1761 }
1762 }
1763
1764 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001765 public void cancelAll() throws RemoteException {
1766 final int uid = Binder.getCallingUid();
1767
1768 long ident = Binder.clearCallingIdentity();
1769 try {
Dianne Hackborne07641d2016-11-09 15:07:23 -08001770 JobSchedulerService.this.cancelJobsForUid(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001771 } finally {
1772 Binder.restoreCallingIdentity(ident);
1773 }
1774 }
1775
1776 @Override
1777 public void cancel(int jobId) throws RemoteException {
1778 final int uid = Binder.getCallingUid();
1779
1780 long ident = Binder.clearCallingIdentity();
1781 try {
1782 JobSchedulerService.this.cancelJob(uid, jobId);
1783 } finally {
1784 Binder.restoreCallingIdentity(ident);
1785 }
1786 }
1787
1788 /**
1789 * "dumpsys" infrastructure
1790 */
1791 @Override
1792 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1793 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1794
1795 long identityToken = Binder.clearCallingIdentity();
1796 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001797 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001798 } finally {
1799 Binder.restoreCallingIdentity(identityToken);
1800 }
1801 }
Christopher Tate5d346052016-03-08 12:56:08 -08001802
1803 @Override
1804 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001805 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08001806 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07001807 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08001808 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001809 };
1810
Christopher Tate5d346052016-03-08 12:56:08 -08001811 // Shell command infrastructure: run the given job immediately
1812 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1813 if (DEBUG) {
1814 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1815 + " " + jobId + " f=" + force);
1816 }
1817
1818 try {
1819 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1820 if (uid < 0) {
1821 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1822 }
1823
1824 synchronized (mLock) {
1825 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1826 if (js == null) {
1827 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1828 }
1829
1830 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1831 if (!js.isConstraintsSatisfied()) {
1832 js.overrideState = 0;
1833 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1834 }
1835
1836 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1837 }
1838 } catch (RemoteException e) {
1839 // can't happen
1840 }
1841 return 0;
1842 }
1843
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001844 void setMonitorBattery(boolean enabled) {
1845 synchronized (mLock) {
1846 if (mBatteryController != null) {
1847 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
1848 }
1849 }
1850 }
1851
1852 int getBatterySeq() {
1853 synchronized (mLock) {
1854 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
1855 }
1856 }
1857
1858 boolean getBatteryCharging() {
1859 synchronized (mLock) {
1860 return mBatteryController != null
1861 ? mBatteryController.getTracker().isOnStablePower() : false;
1862 }
1863 }
1864
1865 boolean getBatteryNotLow() {
1866 synchronized (mLock) {
1867 return mBatteryController != null
1868 ? mBatteryController.getTracker().isBatteryNotLow() : false;
1869 }
1870 }
1871
Dianne Hackborn532ea262017-03-17 17:50:55 -07001872 int getStorageSeq() {
1873 synchronized (mLock) {
1874 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
1875 }
1876 }
1877
1878 boolean getStorageNotLow() {
1879 synchronized (mLock) {
1880 return mStorageController != null
1881 ? mStorageController.getTracker().isStorageNotLow() : false;
1882 }
1883 }
1884
Shreyas Basarge5db09082016-01-07 13:38:29 +00001885 private String printContextIdToJobMap(JobStatus[] map, String initial) {
1886 StringBuilder s = new StringBuilder(initial + ": ");
1887 for (int i=0; i<map.length; i++) {
1888 s.append("(")
1889 .append(map[i] == null? -1: map[i].getJobId())
1890 .append(map[i] == null? -1: map[i].getUid())
1891 .append(")" );
1892 }
1893 return s.toString();
1894 }
1895
1896 private String printPendingQueue() {
1897 StringBuilder s = new StringBuilder("Pending queue: ");
1898 Iterator<JobStatus> it = mPendingJobs.iterator();
1899 while (it.hasNext()) {
1900 JobStatus js = it.next();
1901 s.append("(")
1902 .append(js.getJob().getId())
1903 .append(", ")
1904 .append(js.getUid())
1905 .append(") ");
1906 }
1907 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07001908 }
Christopher Tate7060b042014-06-09 19:50:00 -07001909
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001910 static void dumpHelp(PrintWriter pw) {
1911 pw.println("Job Scheduler (jobscheduler) dump options:");
1912 pw.println(" [-h] [package] ...");
1913 pw.println(" -h: print this help");
1914 pw.println(" [package] is an optional package name to limit the output to.");
1915 }
1916
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001917 void dumpInternal(final PrintWriter pw, String[] args) {
1918 int filterUid = -1;
1919 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001920 int opti = 0;
1921 while (opti < args.length) {
1922 String arg = args[opti];
1923 if ("-h".equals(arg)) {
1924 dumpHelp(pw);
1925 return;
1926 } else if ("-a".equals(arg)) {
1927 // Ignore, we always dump all.
1928 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1929 pw.println("Unknown option: " + arg);
1930 return;
1931 } else {
1932 break;
1933 }
1934 opti++;
1935 }
1936 if (opti < args.length) {
1937 String pkg = args[opti];
1938 try {
1939 filterUid = getContext().getPackageManager().getPackageUid(pkg,
Amith Yamasani0d1fd8d2016-10-12 14:21:51 -07001940 PackageManager.MATCH_ANY_USER);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001941 } catch (NameNotFoundException ignored) {
1942 pw.println("Invalid package: " + pkg);
1943 return;
1944 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001945 }
1946 }
1947
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001948 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07001949 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001950 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001951 mConstants.dump(pw);
1952 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001953 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001954 pw.print("Registered ");
1955 pw.print(mJobs.size());
1956 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07001957 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001958 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1959 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001960 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001961 public int compare(JobStatus o1, JobStatus o2) {
1962 int uid1 = o1.getUid();
1963 int uid2 = o2.getUid();
1964 int id1 = o1.getJobId();
1965 int id2 = o2.getJobId();
1966 if (uid1 != uid2) {
1967 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001968 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001969 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001970 }
1971 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001972 for (JobStatus job : jobs) {
1973 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
1974 pw.println(job.toShortStringExceptUniqueId());
1975
1976 // Skip printing details if the caller requested a filter
1977 if (!job.shouldDump(filterUidFinal)) {
1978 continue;
1979 }
1980
1981 job.dump(pw, " ", true);
1982 pw.print(" Ready: ");
1983 pw.print(mHandler.isReadyToBeExecutedLocked(job));
1984 pw.print(" (job=");
1985 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001986 pw.print(" user=");
1987 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07001988 pw.print(" !pending=");
1989 pw.print(!mPendingJobs.contains(job));
1990 pw.print(" !active=");
1991 pw.print(!isCurrentlyActiveLocked(job));
1992 pw.print(" comp=");
1993 boolean componentPresent = false;
1994 try {
1995 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1996 job.getServiceComponent(),
1997 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1998 job.getUserId()) != null);
1999 } catch (RemoteException e) {
2000 }
2001 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002002 pw.println(")");
2003 }
Christopher Tate7060b042014-06-09 19:50:00 -07002004 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002005 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002006 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002007 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002008 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002009 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07002010 }
2011 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002012 pw.println("Uid priority overrides:");
2013 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002014 int uid = mUidPriorityOverride.keyAt(i);
2015 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2016 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2017 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2018 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002019 }
2020 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002021 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002022 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002023 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2024 pw.println();
2025 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002026 pw.println("Pending queue:");
2027 for (int i=0; i<mPendingJobs.size(); i++) {
2028 JobStatus job = mPendingJobs.get(i);
2029 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2030 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002031 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002032 int priority = evaluateJobPriorityLocked(job);
2033 if (priority != JobInfo.PRIORITY_DEFAULT) {
2034 pw.print(" Evaluated priority: "); pw.println(priority);
2035 }
2036 pw.print(" Tag: "); pw.println(job.getTag());
2037 }
Christopher Tate7060b042014-06-09 19:50:00 -07002038 pw.println();
2039 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002040 for (int i=0; i<mActiveServices.size(); i++) {
2041 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002042 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Shreyas Basarge5db09082016-01-07 13:38:29 +00002043 if (jsc.getRunningJob() == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002044 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07002045 continue;
2046 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002047 pw.println(jsc.getRunningJob().toShortString());
2048 pw.print(" Running for: ");
2049 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
2050 pw.print(", timeout at: ");
2051 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
2052 pw.println();
2053 jsc.getRunningJob().dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002054 int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
2055 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002056 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002057 }
Christopher Tate7060b042014-06-09 19:50:00 -07002058 }
2059 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002060 if (filterUid == -1) {
2061 pw.println();
2062 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2063 pw.print("mReportedActive="); pw.println(mReportedActive);
2064 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2065 }
Christopher Tate7060b042014-06-09 19:50:00 -07002066 }
2067 pw.println();
2068 }
2069}