blob: 5e212b0ef66317a71bafe6bf23bd13d01f199aac [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
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070031import android.app.ActivityManager;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070032import android.app.ActivityManagerNative;
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;
Christopher Tate7060b042014-06-09 19:50:00 -070062import android.os.SystemClock;
63import android.os.UserHandle;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070064import android.provider.Settings;
65import android.util.KeyValueListParser;
Christopher Tate7060b042014-06-09 19:50:00 -070066import android.util.Slog;
67import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080068import android.util.SparseIntArray;
69import android.util.TimeUtils;
Christopher Tate5d346052016-03-08 12:56:08 -080070
Dianne Hackbornfdb19562014-07-11 16:03:36 -070071import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070072import com.android.internal.app.procstats.ProcessStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070073import com.android.internal.util.ArrayUtils;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080074import com.android.server.DeviceIdleController;
75import com.android.server.LocalServices;
Christopher Tate2f36fd62016-02-18 18:36:08 -080076import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080077import com.android.server.job.controllers.AppIdleController;
Christopher Tate7060b042014-06-09 19:50:00 -070078import com.android.server.job.controllers.BatteryController;
79import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080080import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070081import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070082import com.android.server.job.controllers.IdleController;
83import com.android.server.job.controllers.JobStatus;
84import com.android.server.job.controllers.StateController;
85import com.android.server.job.controllers.TimeController;
86
Jeff Sharkey822cbd12016-02-25 11:09:55 -070087import libcore.util.EmptyArray;
88
Christopher Tate7060b042014-06-09 19:50:00 -070089/**
90 * Responsible for taking jobs representing work to be performed by a client app, and determining
91 * based on the criteria specified when that job should be run against the client application's
92 * endpoint.
93 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
94 * about constraints, or the state of active jobs. It receives callbacks from the various
95 * controllers and completed jobs and operates accordingly.
96 *
97 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
98 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
99 * @hide
100 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800101public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700102 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800103 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700104 public static final boolean DEBUG = false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800105
Dianne Hackborn970510b2016-02-24 16:56:42 -0800106 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700107 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800108 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800109 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800110 /** The maximum number of jobs that we allow an unprivileged app to schedule */
111 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700112
Christopher Tate2f36fd62016-02-18 18:36:08 -0800113
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800114 /** Global local for all job scheduler state. */
115 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700116 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700117 final JobStore mJobs;
Dianne Hackborn807de782016-04-07 17:54:41 -0700118 /** Tracking amount of time each package runs for. */
119 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700120
121 static final int MSG_JOB_EXPIRED = 0;
122 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700123 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000124 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700125
Christopher Tate7060b042014-06-09 19:50:00 -0700126 /**
127 * Track Services that have currently active or pending jobs. The index is provided by
128 * {@link JobStatus#getServiceToken()}
129 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700130 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700131 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700132 List<StateController> mControllers;
Christopher Tate7060b042014-06-09 19:50:00 -0700133 /**
134 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
135 * when ready to execute them.
136 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700137 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700138
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700139 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700140
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700141 final JobHandler mHandler;
142 final JobSchedulerStub mJobSchedulerStub;
143
144 IBatteryStats mBatteryStats;
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700145 PowerManager mPowerManager;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800146 DeviceIdleController.LocalService mLocalDeviceIdleController;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700147
148 /**
149 * Set to true once we are allowed to run third party apps.
150 */
151 boolean mReadyToRock;
152
Christopher Tate7060b042014-06-09 19:50:00 -0700153 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800154 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800155 */
156 boolean mReportedActive;
157
158 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800159 * Current limit on the number of concurrent JobServiceContext entries we want to
160 * keep actively running a job.
161 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700162 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800163
164 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800165 * Which uids are currently in the foreground.
166 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800167 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
168
169 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
170
171 /**
172 * This array essentially stores the state of mActiveServices array.
173 * The ith index stores the job present on the ith JobServiceContext.
174 * We manipulate this array until we arrive at what jobs should be running on
175 * what JobServiceContext.
176 */
177 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
178 /**
179 * Indicates whether we need to act on this jobContext id
180 */
181 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
182 /**
183 * The uid whose jobs we would like to assign to a context.
184 */
185 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800186
187 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700188 * All times are in milliseconds. These constants are kept synchronized with the system
189 * global Settings. Any access to this class or its fields should be done while
190 * holding the JobSchedulerService.mLock lock.
191 */
192 private final class Constants extends ContentObserver {
193 // Key names stored in the settings value.
194 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
195 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
196 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
197 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
198 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
199 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
200 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
201 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
202 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
203 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
204 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
205 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
206
207 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
208 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
209 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
210 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
211 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
212 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
213 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
214 private static final int DEFAULT_FG_JOB_COUNT = 4;
215 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
216 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
217 private static final int DEFAULT_BG_LOW_JOB_COUNT = 2;
218 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
219
220 /**
221 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
222 * early.
223 */
224 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
225 /**
226 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
227 * things early.
228 */
229 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
230 /**
231 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
232 * things early. 1 == Run connectivity jobs as soon as ready.
233 */
234 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
235 /**
236 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
237 * schedule things early.
238 */
239 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
240 /**
241 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
242 * running some work early. This (and thus the other min counts) is now set to 1, to
243 * prevent any batching at this level. Since we now do batching through doze, that is
244 * a much better mechanism.
245 */
246 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
247 /**
248 * This is the job execution factor that is considered to be heavy use of the system.
249 */
250 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
251 /**
252 * This is the job execution factor that is considered to be moderate use of the system.
253 */
254 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
255 /**
256 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
257 */
258 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
259 /**
260 * The maximum number of background jobs we allow when the system is in a normal
261 * memory state.
262 */
263 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
264 /**
265 * The maximum number of background jobs we allow when the system is in a moderate
266 * memory state.
267 */
268 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
269 /**
270 * The maximum number of background jobs we allow when the system is in a low
271 * memory state.
272 */
273 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
274 /**
275 * The maximum number of background jobs we allow when the system is in a critical
276 * memory state.
277 */
278 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
279
280 private ContentResolver mResolver;
281 private final KeyValueListParser mParser = new KeyValueListParser(',');
282
283 public Constants(Handler handler) {
284 super(handler);
285 }
286
287 public void start(ContentResolver resolver) {
288 mResolver = resolver;
289 mResolver.registerContentObserver(Settings.Global.getUriFor(
290 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
291 updateConstants();
292 }
293
294 @Override
295 public void onChange(boolean selfChange, Uri uri) {
296 updateConstants();
297 }
298
299 private void updateConstants() {
300 synchronized (mLock) {
301 try {
302 mParser.setString(Settings.Global.getString(mResolver,
303 Settings.Global.ALARM_MANAGER_CONSTANTS));
304 } catch (IllegalArgumentException e) {
305 // Failed to parse the settings string, log this and move on
306 // with defaults.
307 Slog.e(TAG, "Bad device idle settings", e);
308 }
309
310 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
311 DEFAULT_MIN_IDLE_COUNT);
312 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
313 DEFAULT_MIN_CHARGING_COUNT);
314 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
315 DEFAULT_MIN_CONNECTIVITY_COUNT);
316 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
317 DEFAULT_MIN_CONTENT_COUNT);
318 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
319 DEFAULT_MIN_READY_JOBS_COUNT);
320 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
321 DEFAULT_HEAVY_USE_FACTOR);
322 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
323 DEFAULT_MODERATE_USE_FACTOR);
324 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
325 DEFAULT_FG_JOB_COUNT);
326 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
327 DEFAULT_BG_NORMAL_JOB_COUNT);
328 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
329 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
330 }
331 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
332 DEFAULT_BG_MODERATE_JOB_COUNT);
333 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
334 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
335 }
336 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
337 DEFAULT_BG_LOW_JOB_COUNT);
338 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
339 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
340 }
341 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
342 DEFAULT_BG_CRITICAL_JOB_COUNT);
343 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
344 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
345 }
346 }
347 }
348
349 void dump(PrintWriter pw) {
350 pw.println(" Settings:");
351
352 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
353 pw.print(MIN_IDLE_COUNT); pw.println();
354
355 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
356 pw.print(MIN_CHARGING_COUNT); pw.println();
357
358 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
359 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
360
361 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
362 pw.print(MIN_CONTENT_COUNT); pw.println();
363
364 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
365 pw.print(MIN_READY_JOBS_COUNT); pw.println();
366
367 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
368 pw.print(HEAVY_USE_FACTOR); pw.println();
369
370 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
371 pw.print(MODERATE_USE_FACTOR); pw.println();
372
373 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
374 pw.print(FG_JOB_COUNT); pw.println();
375
376 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
377 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
378
379 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
380 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
381
382 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
383 pw.print(BG_LOW_JOB_COUNT); pw.println();
384
385 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
386 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
387 }
388 }
389
390 final Constants mConstants;
391
392 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700393 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
394 * still clean up. On reinstall the package will have a new uid.
395 */
396 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
397 @Override
398 public void onReceive(Context context, Intent intent) {
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700399 if (DEBUG) {
400 Slog.d(TAG, "Receieved: " + intent.getAction());
401 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700402 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
403 // Purge the app's jobs if the whole package was just disabled. When this is
404 // the case the component name will be a bare package name.
405 final String pkgName = getPackageName(intent);
406 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
407 if (pkgName != null && pkgUid != -1) {
408 final String[] changedComponents = intent.getStringArrayExtra(
409 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
410 if (changedComponents != null) {
411 for (String component : changedComponents) {
412 if (component.equals(pkgName)) {
413 if (DEBUG) {
414 Slog.d(TAG, "Package state change: " + pkgName);
415 }
416 try {
417 final int userId = UserHandle.getUserId(pkgUid);
418 IPackageManager pm = AppGlobals.getPackageManager();
419 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
420 if (state == COMPONENT_ENABLED_STATE_DISABLED
421 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
422 if (DEBUG) {
423 Slog.d(TAG, "Removing jobs for package " + pkgName
424 + " in user " + userId);
425 }
426 cancelJobsForUid(pkgUid, true);
427 }
428 } catch (RemoteException e) { /* cannot happen */ }
429 break;
430 }
431 }
432 }
433 } else {
434 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
435 }
436 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700437 // If this is an outright uninstall rather than the first half of an
438 // app update sequence, cancel the jobs associated with the app.
439 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
440 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
441 if (DEBUG) {
442 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
443 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700444 cancelJobsForUid(uidRemoved, true);
Christopher Tate7060b042014-06-09 19:50:00 -0700445 }
Shreyas Basarge5db09082016-01-07 13:38:29 +0000446 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
Christopher Tate7060b042014-06-09 19:50:00 -0700447 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
448 if (DEBUG) {
449 Slog.d(TAG, "Removing jobs for user: " + userId);
450 }
451 cancelJobsForUser(userId);
452 }
453 }
454 };
455
Christopher Tateb5c07882016-05-26 17:11:09 -0700456 private String getPackageName(Intent intent) {
457 Uri uri = intent.getData();
458 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
459 return pkg;
460 }
461
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700462 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
463 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800464 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700465 }
466
467 @Override public void onUidGone(int uid) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800468 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700469 }
470
471 @Override public void onUidActive(int uid) throws RemoteException {
472 }
473
474 @Override public void onUidIdle(int uid) throws RemoteException {
475 cancelJobsForUid(uid, false);
476 }
477 };
478
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800479 public Object getLock() {
480 return mLock;
481 }
482
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700483 public JobStore getJobStore() {
484 return mJobs;
485 }
486
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700487 @Override
488 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700489 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
490 // Let's kick any outstanding jobs for this user.
491 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
492 }
493
494 @Override
495 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700496 // Let's kick any outstanding jobs for this user.
497 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
498 }
499
500 @Override
501 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700502 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700503 }
504
Christopher Tate7060b042014-06-09 19:50:00 -0700505 /**
506 * Entry point from client to schedule the provided job.
507 * This cancels the job if it's already been scheduled, and replaces it with the one provided.
508 * @param job JobInfo object containing execution parameters
509 * @param uId The package identifier of the application this job is for.
Christopher Tate7060b042014-06-09 19:50:00 -0700510 * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
511 */
Matthew Williams900c67f2014-07-09 12:46:53 -0700512 public int schedule(JobInfo job, int uId) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800513 return scheduleAsPackage(job, uId, null, -1, null);
Shreyas Basarge968ac752016-01-11 23:09:26 +0000514 }
515
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800516 public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
517 String tag) {
518 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700519 try {
520 if (ActivityManagerNative.getDefault().getAppStartMode(uId,
521 job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
522 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
523 + " -- package not allowed to start");
524 return JobScheduler.RESULT_FAILURE;
525 }
526 } catch (RemoteException e) {
527 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800528 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
529 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800530 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800531 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800532 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800533 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
534 Slog.w(TAG, "Too many jobs for uid " + uId);
535 throw new IllegalStateException("Apps may not schedule more than "
536 + MAX_JOBS_PER_APP + " distinct jobs");
537 }
538 }
539
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800540 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700541 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700542 cancelJobImpl(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700543 }
544 startTrackingJob(jobStatus, toCancel);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800545 }
Matthew Williamsbafeeb92014-08-08 11:51:06 -0700546 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700547 return JobScheduler.RESULT_SUCCESS;
548 }
549
550 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800551 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800552 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
553 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
554 for (int i = jobs.size() - 1; i >= 0; i--) {
555 JobStatus job = jobs.get(i);
556 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700557 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800558 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700559 }
Christopher Tate7060b042014-06-09 19:50:00 -0700560 }
561
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600562 public JobInfo getPendingJob(int uid, int jobId) {
563 synchronized (mLock) {
564 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
565 for (int i = jobs.size() - 1; i >= 0; i--) {
566 JobStatus job = jobs.get(i);
567 if (job.getJobId() == jobId) {
568 return job.getJob();
569 }
570 }
571 return null;
572 }
573 }
574
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700575 void cancelJobsForUser(int userHandle) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700576 List<JobStatus> jobsForUser;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800577 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700578 jobsForUser = mJobs.getJobsByUser(userHandle);
579 }
580 for (int i=0; i<jobsForUser.size(); i++) {
581 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700582 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700583 }
584 }
585
586 /**
587 * Entry point from client to cancel all jobs originating from their uid.
588 * This will remove the job from the master list, and cancel the job if it was staged for
589 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700590 * @param uid Uid to check against for removal of a job.
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700591 * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
592 * whose apps are stopped.
Christopher Tate7060b042014-06-09 19:50:00 -0700593 */
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700594 public void cancelJobsForUid(int uid, boolean forceAll) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700595 List<JobStatus> jobsForUid;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800596 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700597 jobsForUid = mJobs.getJobsByUid(uid);
598 }
599 for (int i=0; i<jobsForUid.size(); i++) {
600 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700601 if (!forceAll) {
602 String packageName = toRemove.getServiceComponent().getPackageName();
603 try {
604 if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
605 != ActivityManager.APP_START_MODE_DISABLED) {
606 continue;
607 }
608 } catch (RemoteException e) {
609 }
610 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700611 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700612 }
613 }
614
615 /**
616 * Entry point from client to cancel the job corresponding to the jobId provided.
617 * This will remove the job from the master list, and cancel the job if it was staged for
618 * execution or being executed.
619 * @param uid Uid of the calling client.
620 * @param jobId Id of the job, provided at schedule-time.
621 */
622 public void cancelJob(int uid, int jobId) {
623 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800624 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700625 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Matthew Williams48a30db2014-09-23 13:39:36 -0700626 }
627 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700628 cancelJobImpl(toCancel, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700629 }
630 }
631
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700632 private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800633 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700634 stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800635 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700636 // Remove from pending queue.
Dianne Hackborn807de782016-04-07 17:54:41 -0700637 if (mPendingJobs.remove(cancelled)) {
638 mJobPackageTracker.noteNonpending(cancelled);
639 }
Matthew Williams48a30db2014-09-23 13:39:36 -0700640 // Cancel if running.
Shreyas Basarge5db09082016-01-07 13:38:29 +0000641 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800642 reportActive();
Matthew Williams48a30db2014-09-23 13:39:36 -0700643 }
Christopher Tate7060b042014-06-09 19:50:00 -0700644 }
645
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800646 void updateUidState(int uid, int procState) {
647 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800648 if (procState == ActivityManager.PROCESS_STATE_TOP) {
649 // Only use this if we are exactly the top app. All others can live
650 // with just the foreground priority. This means that persistent processes
651 // can never be the top app priority... that is fine.
652 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
653 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
654 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800655 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800656 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800657 }
658 }
659 }
660
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700661 @Override
662 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800663 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700664 if (deviceIdle) {
665 // When becoming idle, make sure no jobs are actively running.
666 for (int i=0; i<mActiveServices.size(); i++) {
667 JobServiceContext jsc = mActiveServices.get(i);
668 final JobStatus executing = jsc.getRunningJob();
669 if (executing != null) {
670 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
671 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700672 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700673 } else {
674 // When coming out of idle, allow thing to start back up.
675 if (mReadyToRock) {
676 if (mLocalDeviceIdleController != null) {
677 if (!mReportedActive) {
678 mReportedActive = true;
679 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700680 }
681 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700682 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700683 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700684 }
685 }
686 }
687
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800688 void reportActive() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000689 // active is true if pending queue contains jobs OR some job is running.
690 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800691 if (mPendingJobs.size() <= 0) {
692 for (int i=0; i<mActiveServices.size(); i++) {
693 JobServiceContext jsc = mActiveServices.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000694 if (jsc.getRunningJob() != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800695 active = true;
696 break;
697 }
698 }
699 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000700
701 if (mReportedActive != active) {
702 mReportedActive = active;
703 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800704 mLocalDeviceIdleController.setJobsActive(active);
705 }
706 }
707 }
708
Christopher Tate7060b042014-06-09 19:50:00 -0700709 /**
710 * Initializes the system service.
711 * <p>
712 * Subclasses must define a single argument constructor that accepts the context
713 * and passes it to super.
714 * </p>
715 *
716 * @param context The system server context.
717 */
718 public JobSchedulerService(Context context) {
719 super(context);
720 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700721 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700722 mControllers.add(ConnectivityController.get(this));
723 mControllers.add(TimeController.get(this));
724 mControllers.add(IdleController.get(this));
725 mControllers.add(BatteryController.get(this));
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800726 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800727 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700728 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700729
730 mHandler = new JobHandler(context.getMainLooper());
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700731 mConstants = new Constants(mHandler);
Christopher Tate7060b042014-06-09 19:50:00 -0700732 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tate7060b042014-06-09 19:50:00 -0700733 mJobs = JobStore.initAndGet(this);
734 }
735
736 @Override
737 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000738 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700739 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
740 }
741
742 @Override
743 public void onBootPhase(int phase) {
744 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700745 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000746 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700747 final IntentFilter filter = new IntentFilter();
748 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
749 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tate7060b042014-06-09 19:50:00 -0700750 filter.addDataScheme("package");
751 getContext().registerReceiverAsUser(
752 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
753 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
754 getContext().registerReceiverAsUser(
755 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000756 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700757 try {
758 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800759 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
760 | ActivityManager.UID_OBSERVER_IDLE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700761 } catch (RemoteException e) {
762 // ignored; both services live in system_server
763 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700764 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800765 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700766 // Let's go!
767 mReadyToRock = true;
768 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
769 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800770 mLocalDeviceIdleController
771 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700772 // Create the "runners".
773 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
774 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700775 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700776 getContext().getMainLooper()));
777 }
778 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800779 mJobs.forEachJob(new JobStatusFunctor() {
780 @Override
781 public void process(JobStatus job) {
782 for (int controller = 0; controller < mControllers.size(); controller++) {
783 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800784 sc.maybeStartTrackingJobLocked(job, null);
785 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700786 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800787 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700788 // GO GO GO!
789 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
790 }
Christopher Tate7060b042014-06-09 19:50:00 -0700791 }
792 }
793
794 /**
795 * Called when we have a job status object that we need to insert in our
796 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
797 * about.
798 */
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800799 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800800 synchronized (mLock) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800801 final boolean update = mJobs.add(jobStatus);
802 if (mReadyToRock) {
803 for (int i = 0; i < mControllers.size(); i++) {
804 StateController controller = mControllers.get(i);
805 if (update) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700806 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800807 }
808 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700809 }
Christopher Tate7060b042014-06-09 19:50:00 -0700810 }
Christopher Tate7060b042014-06-09 19:50:00 -0700811 }
812 }
813
814 /**
815 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
816 * object removed.
817 */
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700818 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
819 boolean writeBack) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800820 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700821 // Remove from store as well as controllers.
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800822 final boolean removed = mJobs.remove(jobStatus, writeBack);
823 if (removed && mReadyToRock) {
824 for (int i=0; i<mControllers.size(); i++) {
825 StateController controller = mControllers.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700826 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800827 }
Christopher Tate7060b042014-06-09 19:50:00 -0700828 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800829 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -0700830 }
Christopher Tate7060b042014-06-09 19:50:00 -0700831 }
832
Shreyas Basarge5db09082016-01-07 13:38:29 +0000833 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700834 for (int i=0; i<mActiveServices.size(); i++) {
835 JobServiceContext jsc = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700836 final JobStatus executing = jsc.getRunningJob();
837 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Shreyas Basarge5db09082016-01-07 13:38:29 +0000838 jsc.cancelExecutingJob(reason);
Christopher Tate7060b042014-06-09 19:50:00 -0700839 return true;
840 }
841 }
842 return false;
843 }
844
845 /**
846 * @param job JobStatus we are querying against.
847 * @return Whether or not the job represented by the status object is currently being run or
848 * is pending.
849 */
850 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700851 for (int i=0; i<mActiveServices.size(); i++) {
852 JobServiceContext serviceContext = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700853 final JobStatus running = serviceContext.getRunningJob();
854 if (running != null && running.matches(job.getUid(), job.getJobId())) {
855 return true;
856 }
857 }
858 return false;
859 }
860
Dianne Hackborn807de782016-04-07 17:54:41 -0700861 void noteJobsPending(List<JobStatus> jobs) {
862 for (int i = jobs.size() - 1; i >= 0; i--) {
863 JobStatus job = jobs.get(i);
864 mJobPackageTracker.notePending(job);
865 }
866 }
867
868 void noteJobsNonpending(List<JobStatus> jobs) {
869 for (int i = jobs.size() - 1; i >= 0; i--) {
870 JobStatus job = jobs.get(i);
871 mJobPackageTracker.noteNonpending(job);
872 }
873 }
874
Christopher Tate7060b042014-06-09 19:50:00 -0700875 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700876 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
877 * specify an override deadline on a failed job (the failed job will run even though it's not
878 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
879 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
880 *
Christopher Tate7060b042014-06-09 19:50:00 -0700881 * @param failureToReschedule Provided job status that we will reschedule.
882 * @return A newly instantiated JobStatus with the same constraints as the last job except
883 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700884 *
885 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
Christopher Tate7060b042014-06-09 19:50:00 -0700886 */
887 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
888 final long elapsedNowMillis = SystemClock.elapsedRealtime();
889 final JobInfo job = failureToReschedule.getJob();
890
891 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -0700892 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
893 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -0700894
895 switch (job.getBackoffPolicy()) {
Matthew Williamsd1c06752014-08-22 14:15:28 -0700896 case JobInfo.BACKOFF_POLICY_LINEAR:
897 delayMillis = initialBackoffMillis * backoffAttempts;
Christopher Tate7060b042014-06-09 19:50:00 -0700898 break;
899 default:
900 if (DEBUG) {
901 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
902 }
Matthew Williamsd1c06752014-08-22 14:15:28 -0700903 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
904 delayMillis =
905 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
Christopher Tate7060b042014-06-09 19:50:00 -0700906 break;
907 }
Matthew Williamsd1c06752014-08-22 14:15:28 -0700908 delayMillis =
909 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800910 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -0700911 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800912 for (int ic=0; ic<mControllers.size(); ic++) {
913 StateController controller = mControllers.get(ic);
914 controller.rescheduleForFailure(newJob, failureToReschedule);
915 }
916 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -0700917 }
918
919 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700920 * Called after a periodic has executed so we can reschedule it. We take the last execution
921 * time of the job to be the time of completion (i.e. the time at which this function is
922 * called).
Christopher Tate7060b042014-06-09 19:50:00 -0700923 * This could be inaccurate b/c the job can run for as long as
924 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
925 * to underscheduling at least, rather than if we had taken the last execution time to be the
926 * start of the execution.
927 * @return A new job representing the execution criteria for this instantiation of the
928 * recurring job.
929 */
930 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
931 final long elapsedNow = SystemClock.elapsedRealtime();
932 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700933 long runEarly = 0L;
934
935 // If this periodic was rescheduled it won't have a deadline.
936 if (periodicToReschedule.hasDeadlineConstraint()) {
937 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
938 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +0000939 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -0700940 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +0000941 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
942 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -0700943
944 if (DEBUG) {
945 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
946 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
947 }
948 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
949 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
950 }
951
952 // JobCompletedListener implementations.
953
954 /**
955 * A job just finished executing. We fetch the
956 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
957 * whether we want to reschedule we readd it to the controllers.
958 * @param jobStatus Completed job.
959 * @param needsReschedule Whether the implementing class should reschedule this job.
960 */
961 @Override
962 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
963 if (DEBUG) {
964 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
965 }
Shreyas Basarge73f10252016-02-11 17:06:13 +0000966 // Do not write back immediately if this is a periodic job. The job may get lost if system
967 // shuts down before it is added back.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700968 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -0700969 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700970 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -0700971 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800972 // We still want to check for jobs to execute, because this job may have
973 // scheduled a new job under the same job id, and now we can run it.
974 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700975 return;
976 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800977 // Note: there is a small window of time in here where, when rescheduling a job,
978 // we will stop monitoring its content providers. This should be fixed by stopping
979 // the old job after scheduling the new one, but since we have no lock held here
980 // that may cause ordering problems if the app removes jobStatus while in here.
Christopher Tate7060b042014-06-09 19:50:00 -0700981 if (needsReschedule) {
982 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800983 startTrackingJob(rescheduled, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -0700984 } else if (jobStatus.getJob().isPeriodic()) {
985 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800986 startTrackingJob(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -0700987 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000988 reportActive();
989 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700990 }
991
992 // StateChangedListener implementations.
993
994 /**
Matthew Williams48a30db2014-09-23 13:39:36 -0700995 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
996 * some controller's state has changed, so as to run through the list of jobs and start/stop
997 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -0700998 */
999 @Override
1000 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001001 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001002 }
1003
1004 @Override
1005 public void onRunJobNow(JobStatus jobStatus) {
1006 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1007 }
1008
Christopher Tate7060b042014-06-09 19:50:00 -07001009 private class JobHandler extends Handler {
1010
1011 public JobHandler(Looper looper) {
1012 super(looper);
1013 }
1014
1015 @Override
1016 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001017 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001018 if (!mReadyToRock) {
1019 return;
1020 }
1021 }
Christopher Tate7060b042014-06-09 19:50:00 -07001022 switch (message.what) {
1023 case MSG_JOB_EXPIRED:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001024 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001025 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001026 // runNow can be null, which is a controller's way of indicating that its
1027 // state is such that all ready jobs should be run immediately.
Matthew Williams48a30db2014-09-23 13:39:36 -07001028 if (runNow != null && !mPendingJobs.contains(runNow)
1029 && mJobs.containsJob(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001030 mJobPackageTracker.notePending(runNow);
Christopher Tate7060b042014-06-09 19:50:00 -07001031 mPendingJobs.add(runNow);
1032 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001033 queueReadyJobsForExecutionLockedH();
Christopher Tate7060b042014-06-09 19:50:00 -07001034 }
Christopher Tate7060b042014-06-09 19:50:00 -07001035 break;
1036 case MSG_CHECK_JOB:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001037 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001038 if (mReportedActive) {
1039 // if jobs are currently being run, queue all ready jobs for execution.
1040 queueReadyJobsForExecutionLockedH();
1041 } else {
1042 // Check the list of jobs and run some of them if we feel inclined.
1043 maybeQueueReadyJobsForExecutionLockedH();
1044 }
1045 }
1046 break;
1047 case MSG_CHECK_JOB_GREEDY:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001048 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001049 queueReadyJobsForExecutionLockedH();
Matthew Williams48a30db2014-09-23 13:39:36 -07001050 }
Christopher Tate7060b042014-06-09 19:50:00 -07001051 break;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001052 case MSG_STOP_JOB:
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001053 cancelJobImpl((JobStatus)message.obj, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001054 break;
Christopher Tate7060b042014-06-09 19:50:00 -07001055 }
1056 maybeRunPendingJobsH();
1057 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1058 removeMessages(MSG_CHECK_JOB);
1059 }
1060
1061 /**
1062 * Run through list of jobs and execute all possible - at least one is expired so we do
1063 * as many as we can.
1064 */
Matthew Williams48a30db2014-09-23 13:39:36 -07001065 private void queueReadyJobsForExecutionLockedH() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001066 if (DEBUG) {
1067 Slog.d(TAG, "queuing all ready jobs for execution:");
1068 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001069 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001070 mPendingJobs.clear();
1071 mJobs.forEachJob(mReadyQueueFunctor);
1072 mReadyQueueFunctor.postProcess();
1073
Matthew Williams48a30db2014-09-23 13:39:36 -07001074 if (DEBUG) {
1075 final int queuedJobs = mPendingJobs.size();
1076 if (queuedJobs == 0) {
1077 Slog.d(TAG, "No jobs pending.");
1078 } else {
1079 Slog.d(TAG, queuedJobs + " jobs queued.");
Matthew Williams75fc5252014-09-02 16:17:53 -07001080 }
Christopher Tate7060b042014-06-09 19:50:00 -07001081 }
1082 }
1083
Christopher Tate2f36fd62016-02-18 18:36:08 -08001084 class ReadyJobQueueFunctor implements JobStatusFunctor {
1085 ArrayList<JobStatus> newReadyJobs;
1086
1087 @Override
1088 public void process(JobStatus job) {
1089 if (isReadyToBeExecutedLocked(job)) {
1090 if (DEBUG) {
1091 Slog.d(TAG, " queued " + job.toShortString());
1092 }
1093 if (newReadyJobs == null) {
1094 newReadyJobs = new ArrayList<JobStatus>();
1095 }
1096 newReadyJobs.add(job);
1097 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1098 stopJobOnServiceContextLocked(job,
1099 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1100 }
1101 }
1102
1103 public void postProcess() {
1104 if (newReadyJobs != null) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001105 noteJobsPending(newReadyJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001106 mPendingJobs.addAll(newReadyJobs);
1107 }
1108 newReadyJobs = null;
1109 }
1110 }
1111 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1112
Christopher Tate7060b042014-06-09 19:50:00 -07001113 /**
1114 * The state of at least one job has changed. Here is where we could enforce various
1115 * policies on when we want to execute jobs.
1116 * Right now the policy is such:
1117 * If >1 of the ready jobs is idle mode we send all of them off
1118 * if more than 2 network connectivity jobs are ready we send them all off.
1119 * If more than 4 jobs total are ready we send them all off.
1120 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1121 */
Christopher Tate2f36fd62016-02-18 18:36:08 -08001122 class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1123 int chargingCount;
1124 int idleCount;
1125 int backoffCount;
1126 int connectivityCount;
1127 int contentCount;
1128 List<JobStatus> runnableJobs;
1129
1130 public MaybeReadyJobQueueFunctor() {
1131 reset();
1132 }
1133
1134 // Functor method invoked for each job via JobStore.forEachJob()
1135 @Override
1136 public void process(JobStatus job) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001137 if (isReadyToBeExecutedLocked(job)) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001138 try {
1139 if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
1140 job.getJob().getService().getPackageName())
1141 == ActivityManager.APP_START_MODE_DISABLED) {
1142 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1143 + job.getJob().toString() + " -- package not allowed to start");
1144 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001145 return;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001146 }
1147 } catch (RemoteException e) {
1148 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001149 if (job.getNumFailures() > 0) {
1150 backoffCount++;
Christopher Tate7060b042014-06-09 19:50:00 -07001151 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001152 if (job.hasIdleConstraint()) {
1153 idleCount++;
1154 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001155 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1156 || job.hasNotRoamingConstraint()) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001157 connectivityCount++;
1158 }
1159 if (job.hasChargingConstraint()) {
1160 chargingCount++;
1161 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001162 if (job.hasContentTriggerConstraint()) {
1163 contentCount++;
1164 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001165 if (runnableJobs == null) {
1166 runnableJobs = new ArrayList<>();
1167 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001168 runnableJobs.add(job);
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001169 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001170 stopJobOnServiceContextLocked(job,
1171 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001172 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001173 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001174
1175 public void postProcess() {
1176 if (backoffCount > 0 ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001177 idleCount >= mConstants.MIN_IDLE_COUNT ||
1178 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1179 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1180 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1181 (runnableJobs != null
1182 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001183 if (DEBUG) {
1184 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1185 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001186 noteJobsPending(runnableJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001187 mPendingJobs.addAll(runnableJobs);
1188 } else {
1189 if (DEBUG) {
1190 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1191 }
Christopher Tate7060b042014-06-09 19:50:00 -07001192 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001193
1194 // Be ready for next time
1195 reset();
Matthew Williams48a30db2014-09-23 13:39:36 -07001196 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001197
1198 private void reset() {
1199 chargingCount = 0;
1200 idleCount = 0;
1201 backoffCount = 0;
1202 connectivityCount = 0;
1203 contentCount = 0;
1204 runnableJobs = null;
1205 }
1206 }
1207 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1208
1209 private void maybeQueueReadyJobsForExecutionLockedH() {
1210 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1211
Dianne Hackborn807de782016-04-07 17:54:41 -07001212 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001213 mPendingJobs.clear();
1214 mJobs.forEachJob(mMaybeQueueFunctor);
1215 mMaybeQueueFunctor.postProcess();
Christopher Tate7060b042014-06-09 19:50:00 -07001216 }
1217
1218 /**
1219 * Criteria for moving a job into the pending queue:
1220 * - It's ready.
1221 * - It's not pending.
1222 * - It's not already running on a JSC.
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001223 * - The user that requested the job is running.
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001224 * - The component is enabled and runnable.
Christopher Tate7060b042014-06-09 19:50:00 -07001225 */
1226 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001227 final boolean jobReady = job.isReady();
1228 final boolean jobPending = mPendingJobs.contains(job);
1229 final boolean jobActive = isCurrentlyActiveLocked(job);
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001230
1231 final int userId = job.getUserId();
1232 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1233 final boolean componentPresent;
1234 try {
1235 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1236 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1237 userId) != null);
1238 } catch (RemoteException e) {
1239 throw e.rethrowAsRuntimeException();
1240 }
1241
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001242 if (DEBUG) {
1243 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1244 + " ready=" + jobReady + " pending=" + jobPending
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001245 + " active=" + jobActive + " userStarted=" + userStarted
1246 + " componentPresent=" + componentPresent);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001247 }
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001248 return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
Christopher Tate7060b042014-06-09 19:50:00 -07001249 }
1250
1251 /**
1252 * Criteria for cancelling an active job:
1253 * - It's not ready
1254 * - It's running on a JSC.
1255 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001256 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
Christopher Tate7060b042014-06-09 19:50:00 -07001257 return !job.isReady() && isCurrentlyActiveLocked(job);
1258 }
1259
1260 /**
1261 * Reconcile jobs in the pending queue against available execution contexts.
1262 * A controller can force a job into the pending queue even if it's already running, but
1263 * here is where we decide whether to actually execute it.
1264 */
1265 private void maybeRunPendingJobsH() {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001266 synchronized (mLock) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001267 if (DEBUG) {
1268 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1269 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001270 assignJobsToContextsLocked();
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001271 reportActive();
Christopher Tate7060b042014-06-09 19:50:00 -07001272 }
1273 }
1274 }
1275
Dianne Hackborn807de782016-04-07 17:54:41 -07001276 private int adjustJobPriority(int curPriority, JobStatus job) {
1277 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1278 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001279 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001280 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001281 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001282 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1283 }
1284 }
1285 return curPriority;
1286 }
1287
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001288 private int evaluateJobPriorityLocked(JobStatus job) {
1289 int priority = job.getPriority();
1290 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001291 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001292 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001293 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1294 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001295 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001296 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001297 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001298 }
1299
Christopher Tate7060b042014-06-09 19:50:00 -07001300 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001301 * Takes jobs from pending queue and runs them on available contexts.
1302 * If no contexts are available, preempts lower priority jobs to
1303 * run higher priority ones.
1304 * Lock on mJobs before calling this function.
1305 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001306 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001307 if (DEBUG) {
1308 Slog.d(TAG, printPendingQueue());
1309 }
1310
Dianne Hackborn970510b2016-02-24 16:56:42 -08001311 int memLevel;
1312 try {
1313 memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
1314 } catch (RemoteException e) {
1315 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1316 }
1317 switch (memLevel) {
1318 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001319 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001320 break;
1321 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001322 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001323 break;
1324 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001325 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001326 break;
1327 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001328 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001329 break;
1330 }
1331
1332 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1333 boolean[] act = mTmpAssignAct;
1334 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1335 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001336 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001337 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1338 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001339 final JobStatus status = js.getRunningJob();
1340 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001341 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001342 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1343 numForeground++;
1344 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001345 }
1346 act[i] = false;
1347 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001348 }
1349 if (DEBUG) {
1350 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1351 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001352 for (int i=0; i<mPendingJobs.size(); i++) {
1353 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001354
1355 // If job is already running, go to next job.
1356 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1357 if (jobRunningContext != -1) {
1358 continue;
1359 }
1360
Dianne Hackborn970510b2016-02-24 16:56:42 -08001361 final int priority = evaluateJobPriorityLocked(nextPending);
1362 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001363
Shreyas Basarge5db09082016-01-07 13:38:29 +00001364 // Find a context for nextPending. The context should be available OR
1365 // it should have lowest priority among all running jobs
1366 // (sharing the same Uid as nextPending)
1367 int minPriority = Integer.MAX_VALUE;
1368 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001369 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1370 JobStatus job = contextIdToJobMap[j];
1371 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001372 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001373 if ((numActive < mMaxActiveJobs ||
1374 (priority >= JobInfo.PRIORITY_TOP_APP &&
1375 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001376 (preferredUid == nextPending.getUid() ||
1377 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1378 // This slot is free, and we haven't yet hit the limit on
1379 // concurrent jobs... we can just throw the job in to here.
1380 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001381 break;
1382 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001383 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001384 // the context has a preferred Uid or we have reached the limit on
1385 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001386 continue;
1387 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001388 if (job.getUid() != nextPending.getUid()) {
1389 continue;
1390 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001391 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001392 continue;
1393 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001394 if (minPriority > nextPending.lastEvaluatedPriority) {
1395 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001396 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001397 }
1398 }
1399 if (minPriorityContextId != -1) {
1400 contextIdToJobMap[minPriorityContextId] = nextPending;
1401 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001402 numActive++;
1403 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1404 numForeground++;
1405 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001406 }
1407 }
1408 if (DEBUG) {
1409 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1410 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001411 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001412 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001413 boolean preservePreferredUid = false;
1414 if (act[i]) {
1415 JobStatus js = mActiveServices.get(i).getRunningJob();
1416 if (js != null) {
1417 if (DEBUG) {
1418 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1419 }
1420 // preferredUid will be set to uid of currently running job.
1421 mActiveServices.get(i).preemptExecutingJob();
1422 preservePreferredUid = true;
1423 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001424 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001425 if (DEBUG) {
1426 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001427 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001428 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001429 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001430 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001431 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001432 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1433 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001434 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001435 if (mPendingJobs.remove(pendingJob)) {
1436 mJobPackageTracker.noteNonpending(pendingJob);
1437 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001438 }
1439 }
1440 if (!preservePreferredUid) {
1441 mActiveServices.get(i).clearPreferredUid();
1442 }
1443 }
1444 }
1445
1446 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1447 for (int i=0; i<map.length; i++) {
1448 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1449 return i;
1450 }
1451 }
1452 return -1;
1453 }
1454
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001455 final class LocalService implements JobSchedulerInternal {
1456
1457 /**
1458 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1459 * jobs are always considered pending.
1460 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001461 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001462 public List<JobInfo> getSystemScheduledPendingJobs() {
1463 synchronized (mLock) {
1464 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1465 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1466 @Override
1467 public void process(JobStatus job) {
1468 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1469 pendingJobs.add(job.getJob());
1470 }
1471 }
1472 });
1473 return pendingJobs;
1474 }
1475 }
1476 }
1477
Shreyas Basarge5db09082016-01-07 13:38:29 +00001478 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001479 * Binder stub trampoline implementation
1480 */
1481 final class JobSchedulerStub extends IJobScheduler.Stub {
1482 /** Cache determination of whether a given app can persist jobs
1483 * key is uid of the calling app; value is undetermined/true/false
1484 */
1485 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1486
1487 // Enforce that only the app itself (or shared uid participant) can schedule a
1488 // job that runs one of the app's services, as well as verifying that the
1489 // named service properly requires the BIND_JOB_SERVICE permission
1490 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001491 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001492 final ComponentName service = job.getService();
1493 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001494 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001495 PackageManager.MATCH_DIRECT_BOOT_AWARE
1496 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001497 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001498 if (si == null) {
1499 throw new IllegalArgumentException("No such service " + service);
1500 }
Christopher Tate7060b042014-06-09 19:50:00 -07001501 if (si.applicationInfo.uid != uid) {
1502 throw new IllegalArgumentException("uid " + uid +
1503 " cannot schedule job in " + service.getPackageName());
1504 }
1505 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1506 throw new IllegalArgumentException("Scheduled service " + service
1507 + " does not require android.permission.BIND_JOB_SERVICE permission");
1508 }
Christopher Tate5568f542014-06-18 13:53:31 -07001509 } catch (RemoteException e) {
1510 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001511 }
1512 }
1513
1514 private boolean canPersistJobs(int pid, int uid) {
1515 // If we get this far we're good to go; all we need to do now is check
1516 // whether the app is allowed to persist its scheduled work.
1517 final boolean canPersist;
1518 synchronized (mPersistCache) {
1519 Boolean cached = mPersistCache.get(uid);
1520 if (cached != null) {
1521 canPersist = cached.booleanValue();
1522 } else {
1523 // Persisting jobs is tantamount to running at boot, so we permit
1524 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1525 // permission
1526 int result = getContext().checkPermission(
1527 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1528 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1529 mPersistCache.put(uid, canPersist);
1530 }
1531 }
1532 return canPersist;
1533 }
1534
1535 // IJobScheduler implementation
1536 @Override
1537 public int schedule(JobInfo job) throws RemoteException {
1538 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001539 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001540 }
1541 final int pid = Binder.getCallingPid();
1542 final int uid = Binder.getCallingUid();
1543
1544 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001545 if (job.isPersisted()) {
1546 if (!canPersistJobs(pid, uid)) {
1547 throw new IllegalArgumentException("Error: requested job be persisted without"
1548 + " holding RECEIVE_BOOT_COMPLETED permission.");
1549 }
1550 }
Christopher Tate7060b042014-06-09 19:50:00 -07001551
1552 long ident = Binder.clearCallingIdentity();
1553 try {
Matthew Williams900c67f2014-07-09 12:46:53 -07001554 return JobSchedulerService.this.schedule(job, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001555 } finally {
1556 Binder.restoreCallingIdentity(ident);
1557 }
1558 }
1559
1560 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001561 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001562 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001563 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001564 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001565 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1566 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001567 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001568
1569 if (packageName == null) {
1570 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001571 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001572
1573 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1574 android.Manifest.permission.UPDATE_DEVICE_STATS);
1575 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1576 throw new SecurityException("Caller uid " + callerUid
1577 + " not permitted to schedule jobs for other apps");
1578 }
1579
Jeff Sharkey4f100402016-05-03 17:44:23 -06001580 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1581 getContext().enforceCallingOrSelfPermission(
1582 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1583 }
1584
Shreyas Basarge968ac752016-01-11 23:09:26 +00001585 long ident = Binder.clearCallingIdentity();
1586 try {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001587 return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001588 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001589 } finally {
1590 Binder.restoreCallingIdentity(ident);
1591 }
1592 }
1593
1594 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001595 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1596 final int uid = Binder.getCallingUid();
1597
1598 long ident = Binder.clearCallingIdentity();
1599 try {
1600 return JobSchedulerService.this.getPendingJobs(uid);
1601 } finally {
1602 Binder.restoreCallingIdentity(ident);
1603 }
1604 }
1605
1606 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001607 public JobInfo getPendingJob(int jobId) throws RemoteException {
1608 final int uid = Binder.getCallingUid();
1609
1610 long ident = Binder.clearCallingIdentity();
1611 try {
1612 return JobSchedulerService.this.getPendingJob(uid, jobId);
1613 } finally {
1614 Binder.restoreCallingIdentity(ident);
1615 }
1616 }
1617
1618 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001619 public void cancelAll() throws RemoteException {
1620 final int uid = Binder.getCallingUid();
1621
1622 long ident = Binder.clearCallingIdentity();
1623 try {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001624 JobSchedulerService.this.cancelJobsForUid(uid, true);
Christopher Tate7060b042014-06-09 19:50:00 -07001625 } finally {
1626 Binder.restoreCallingIdentity(ident);
1627 }
1628 }
1629
1630 @Override
1631 public void cancel(int jobId) throws RemoteException {
1632 final int uid = Binder.getCallingUid();
1633
1634 long ident = Binder.clearCallingIdentity();
1635 try {
1636 JobSchedulerService.this.cancelJob(uid, jobId);
1637 } finally {
1638 Binder.restoreCallingIdentity(ident);
1639 }
1640 }
1641
1642 /**
1643 * "dumpsys" infrastructure
1644 */
1645 @Override
1646 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1647 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1648
1649 long identityToken = Binder.clearCallingIdentity();
1650 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001651 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001652 } finally {
1653 Binder.restoreCallingIdentity(identityToken);
1654 }
1655 }
Christopher Tate5d346052016-03-08 12:56:08 -08001656
1657 @Override
1658 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1659 String[] args, ResultReceiver resultReceiver) throws RemoteException {
1660 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
1661 this, in, out, err, args, resultReceiver);
1662 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001663 };
1664
Christopher Tate5d346052016-03-08 12:56:08 -08001665 // Shell command infrastructure: run the given job immediately
1666 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1667 if (DEBUG) {
1668 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1669 + " " + jobId + " f=" + force);
1670 }
1671
1672 try {
1673 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1674 if (uid < 0) {
1675 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1676 }
1677
1678 synchronized (mLock) {
1679 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1680 if (js == null) {
1681 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1682 }
1683
1684 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1685 if (!js.isConstraintsSatisfied()) {
1686 js.overrideState = 0;
1687 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1688 }
1689
1690 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1691 }
1692 } catch (RemoteException e) {
1693 // can't happen
1694 }
1695 return 0;
1696 }
1697
Shreyas Basarge5db09082016-01-07 13:38:29 +00001698 private String printContextIdToJobMap(JobStatus[] map, String initial) {
1699 StringBuilder s = new StringBuilder(initial + ": ");
1700 for (int i=0; i<map.length; i++) {
1701 s.append("(")
1702 .append(map[i] == null? -1: map[i].getJobId())
1703 .append(map[i] == null? -1: map[i].getUid())
1704 .append(")" );
1705 }
1706 return s.toString();
1707 }
1708
1709 private String printPendingQueue() {
1710 StringBuilder s = new StringBuilder("Pending queue: ");
1711 Iterator<JobStatus> it = mPendingJobs.iterator();
1712 while (it.hasNext()) {
1713 JobStatus js = it.next();
1714 s.append("(")
1715 .append(js.getJob().getId())
1716 .append(", ")
1717 .append(js.getUid())
1718 .append(") ");
1719 }
1720 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07001721 }
Christopher Tate7060b042014-06-09 19:50:00 -07001722
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001723 static void dumpHelp(PrintWriter pw) {
1724 pw.println("Job Scheduler (jobscheduler) dump options:");
1725 pw.println(" [-h] [package] ...");
1726 pw.println(" -h: print this help");
1727 pw.println(" [package] is an optional package name to limit the output to.");
1728 }
1729
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001730 void dumpInternal(final PrintWriter pw, String[] args) {
1731 int filterUid = -1;
1732 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001733 int opti = 0;
1734 while (opti < args.length) {
1735 String arg = args[opti];
1736 if ("-h".equals(arg)) {
1737 dumpHelp(pw);
1738 return;
1739 } else if ("-a".equals(arg)) {
1740 // Ignore, we always dump all.
1741 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1742 pw.println("Unknown option: " + arg);
1743 return;
1744 } else {
1745 break;
1746 }
1747 opti++;
1748 }
1749 if (opti < args.length) {
1750 String pkg = args[opti];
1751 try {
1752 filterUid = getContext().getPackageManager().getPackageUid(pkg,
1753 PackageManager.MATCH_UNINSTALLED_PACKAGES);
1754 } catch (NameNotFoundException ignored) {
1755 pw.println("Invalid package: " + pkg);
1756 return;
1757 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001758 }
1759 }
1760
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001761 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07001762 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001763 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001764 mConstants.dump(pw);
1765 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001766 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001767 pw.print("Registered ");
1768 pw.print(mJobs.size());
1769 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07001770 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001771 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1772 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001773 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001774 public int compare(JobStatus o1, JobStatus o2) {
1775 int uid1 = o1.getUid();
1776 int uid2 = o2.getUid();
1777 int id1 = o1.getJobId();
1778 int id2 = o2.getJobId();
1779 if (uid1 != uid2) {
1780 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001781 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001782 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001783 }
1784 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001785 for (JobStatus job : jobs) {
1786 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
1787 pw.println(job.toShortStringExceptUniqueId());
1788
1789 // Skip printing details if the caller requested a filter
1790 if (!job.shouldDump(filterUidFinal)) {
1791 continue;
1792 }
1793
1794 job.dump(pw, " ", true);
1795 pw.print(" Ready: ");
1796 pw.print(mHandler.isReadyToBeExecutedLocked(job));
1797 pw.print(" (job=");
1798 pw.print(job.isReady());
1799 pw.print(" pending=");
1800 pw.print(mPendingJobs.contains(job));
1801 pw.print(" active=");
1802 pw.print(isCurrentlyActiveLocked(job));
1803 pw.print(" user=");
1804 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
1805 pw.println(")");
1806 }
Christopher Tate7060b042014-06-09 19:50:00 -07001807 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07001808 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07001809 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001810 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07001811 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001812 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07001813 }
1814 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001815 pw.println("Uid priority overrides:");
1816 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001817 int uid = mUidPriorityOverride.keyAt(i);
1818 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
1819 pw.print(" "); pw.print(UserHandle.formatUid(uid));
1820 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
1821 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001822 }
1823 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001824 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07001825 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001826 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
1827 pw.println();
1828 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001829 pw.println("Pending queue:");
1830 for (int i=0; i<mPendingJobs.size(); i++) {
1831 JobStatus job = mPendingJobs.get(i);
1832 pw.print(" Pending #"); pw.print(i); pw.print(": ");
1833 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08001834 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001835 int priority = evaluateJobPriorityLocked(job);
1836 if (priority != JobInfo.PRIORITY_DEFAULT) {
1837 pw.print(" Evaluated priority: "); pw.println(priority);
1838 }
1839 pw.print(" Tag: "); pw.println(job.getTag());
1840 }
Christopher Tate7060b042014-06-09 19:50:00 -07001841 pw.println();
1842 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001843 for (int i=0; i<mActiveServices.size(); i++) {
1844 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001845 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Shreyas Basarge5db09082016-01-07 13:38:29 +00001846 if (jsc.getRunningJob() == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001847 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07001848 continue;
1849 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001850 pw.println(jsc.getRunningJob().toShortString());
1851 pw.print(" Running for: ");
1852 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
1853 pw.print(", timeout at: ");
1854 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
1855 pw.println();
1856 jsc.getRunningJob().dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001857 int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
1858 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001859 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001860 }
Christopher Tate7060b042014-06-09 19:50:00 -07001861 }
1862 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001863 if (filterUid == -1) {
1864 pw.println();
1865 pw.print("mReadyToRock="); pw.println(mReadyToRock);
1866 pw.print("mReportedActive="); pw.println(mReportedActive);
1867 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
1868 }
Christopher Tate7060b042014-06-09 19:50:00 -07001869 }
1870 pw.println();
1871 }
1872}