blob: c08f866086568b7061569a46c93c74e85620b805 [file] [log] [blame]
Christopher Tate7060b042014-06-09 19:50:00 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.job;
18
Christopher Tateb5c07882016-05-26 17:11:09 -070019import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
Shreyas Basarge5db09082016-01-07 13:38:29 +000022import java.io.FileDescriptor;
23import java.io.PrintWriter;
24import java.util.ArrayList;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070025import java.util.Arrays;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070026import java.util.Collections;
27import java.util.Comparator;
Shreyas Basarge5db09082016-01-07 13:38:29 +000028import java.util.Iterator;
29import java.util.List;
30
Christopher Tateee7805b2016-07-15 16:56:56 -070031import android.app.Activity;
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070032import android.app.ActivityManager;
Christopher Tate5568f542014-06-18 13:53:31 -070033import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070034import android.app.IUidObserver;
Christopher Tate7060b042014-06-09 19:50:00 -070035import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000036import android.app.job.JobParameters;
Christopher Tate7060b042014-06-09 19:50:00 -070037import android.app.job.JobScheduler;
38import android.app.job.JobService;
Shreyas Basarge5db09082016-01-07 13:38:29 +000039import android.app.job.IJobScheduler;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070040import android.app.job.JobWorkItem;
Christopher Tate7060b042014-06-09 19:50:00 -070041import android.content.BroadcastReceiver;
42import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070043import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070044import android.content.Context;
45import android.content.Intent;
46import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070047import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070048import android.content.pm.PackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070049import android.content.pm.ServiceInfo;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060050import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070051import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070052import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070053import android.os.BatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070054import android.os.Binder;
55import android.os.Handler;
56import android.os.Looper;
57import android.os.Message;
Shreyas Basargecbf5ae92016-03-08 16:13:06 +000058import android.os.Process;
Dianne Hackborn88e98df2015-03-23 13:29:14 -070059import android.os.PowerManager;
Christopher Tate7060b042014-06-09 19:50:00 -070060import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080061import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070062import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070063import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070064import android.os.SystemClock;
65import android.os.UserHandle;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070066import android.provider.Settings;
67import android.util.KeyValueListParser;
Christopher Tate7060b042014-06-09 19:50:00 -070068import android.util.Slog;
69import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080070import android.util.SparseIntArray;
71import android.util.TimeUtils;
Christopher Tate5d346052016-03-08 12:56:08 -080072
Dianne Hackbornfdb19562014-07-11 16:03:36 -070073import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070074import com.android.internal.app.procstats.ProcessStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070075import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060076import com.android.internal.util.DumpUtils;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080077import com.android.server.DeviceIdleController;
78import com.android.server.LocalServices;
Christopher Tate2f36fd62016-02-18 18:36:08 -080079import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080080import com.android.server.job.controllers.AppIdleController;
Christopher Tate7060b042014-06-09 19:50:00 -070081import com.android.server.job.controllers.BatteryController;
82import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080083import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070084import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070085import com.android.server.job.controllers.IdleController;
86import com.android.server.job.controllers.JobStatus;
87import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070088import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -070089import com.android.server.job.controllers.TimeController;
90
Jeff Sharkey822cbd12016-02-25 11:09:55 -070091import libcore.util.EmptyArray;
92
Christopher Tate7060b042014-06-09 19:50:00 -070093/**
94 * Responsible for taking jobs representing work to be performed by a client app, and determining
95 * based on the criteria specified when that job should be run against the client application's
96 * endpoint.
97 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
98 * about constraints, or the state of active jobs. It receives callbacks from the various
99 * controllers and completed jobs and operates accordingly.
100 *
101 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
102 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
103 * @hide
104 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800105public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700106 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800107 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700108 public static final boolean DEBUG = false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800109
Dianne Hackborn970510b2016-02-24 16:56:42 -0800110 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700111 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800112 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800113 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800114 /** The maximum number of jobs that we allow an unprivileged app to schedule */
115 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700116
Christopher Tate2f36fd62016-02-18 18:36:08 -0800117
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800118 /** Global local for all job scheduler state. */
119 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700120 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700121 final JobStore mJobs;
Dianne Hackborn807de782016-04-07 17:54:41 -0700122 /** Tracking amount of time each package runs for. */
123 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700124
125 static final int MSG_JOB_EXPIRED = 0;
126 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700127 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000128 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700129
Christopher Tate7060b042014-06-09 19:50:00 -0700130 /**
131 * Track Services that have currently active or pending jobs. The index is provided by
132 * {@link JobStatus#getServiceToken()}
133 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700134 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700135 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700136 List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800137 /** Need direct access to this for testing. */
138 BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700139 /** Need direct access to this for testing. */
140 StorageController mStorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700141 /**
142 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
143 * when ready to execute them.
144 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700145 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700146
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700147 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700148
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700149 final JobHandler mHandler;
150 final JobSchedulerStub mJobSchedulerStub;
151
152 IBatteryStats mBatteryStats;
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700153 PowerManager mPowerManager;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800154 DeviceIdleController.LocalService mLocalDeviceIdleController;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700155
156 /**
157 * Set to true once we are allowed to run third party apps.
158 */
159 boolean mReadyToRock;
160
Christopher Tate7060b042014-06-09 19:50:00 -0700161 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800162 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800163 */
164 boolean mReportedActive;
165
166 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800167 * Current limit on the number of concurrent JobServiceContext entries we want to
168 * keep actively running a job.
169 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700170 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800171
172 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800173 * Which uids are currently in the foreground.
174 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800175 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
176
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700177 /**
178 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
179 */
180 final SparseIntArray mBackingUpUids = new SparseIntArray();
181
Dianne Hackborn970510b2016-02-24 16:56:42 -0800182 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
183
184 /**
185 * This array essentially stores the state of mActiveServices array.
186 * The ith index stores the job present on the ith JobServiceContext.
187 * We manipulate this array until we arrive at what jobs should be running on
188 * what JobServiceContext.
189 */
190 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
191 /**
192 * Indicates whether we need to act on this jobContext id
193 */
194 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
195 /**
196 * The uid whose jobs we would like to assign to a context.
197 */
198 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800199
200 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700201 * All times are in milliseconds. These constants are kept synchronized with the system
202 * global Settings. Any access to this class or its fields should be done while
203 * holding the JobSchedulerService.mLock lock.
204 */
205 private final class Constants extends ContentObserver {
206 // Key names stored in the settings value.
207 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
208 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800209 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700210 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700211 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
212 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
213 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
214 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
215 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
216 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
217 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
218 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
219 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
220 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700221 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
222 = "max_standard_reschedule_count";
223 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
224 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
225 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700226
227 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
228 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800229 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700230 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700231 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
232 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
233 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
234 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
235 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
236 private static final int DEFAULT_FG_JOB_COUNT = 4;
237 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
238 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700239 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700240 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700241 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
242 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
243 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
244 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700245
246 /**
247 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
248 * early.
249 */
250 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
251 /**
252 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
253 * things early.
254 */
255 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
256 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800257 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
258 * schedule things early.
259 */
260 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
261 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700262 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
263 * schedule things early.
264 */
265 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
266 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700267 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
268 * things early. 1 == Run connectivity jobs as soon as ready.
269 */
270 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
271 /**
272 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
273 * schedule things early.
274 */
275 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
276 /**
277 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
278 * running some work early. This (and thus the other min counts) is now set to 1, to
279 * prevent any batching at this level. Since we now do batching through doze, that is
280 * a much better mechanism.
281 */
282 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
283 /**
284 * This is the job execution factor that is considered to be heavy use of the system.
285 */
286 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
287 /**
288 * This is the job execution factor that is considered to be moderate use of the system.
289 */
290 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
291 /**
292 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
293 */
294 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
295 /**
296 * The maximum number of background jobs we allow when the system is in a normal
297 * memory state.
298 */
299 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
300 /**
301 * The maximum number of background jobs we allow when the system is in a moderate
302 * memory state.
303 */
304 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
305 /**
306 * The maximum number of background jobs we allow when the system is in a low
307 * memory state.
308 */
309 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
310 /**
311 * The maximum number of background jobs we allow when the system is in a critical
312 * memory state.
313 */
314 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700315 /**
316 * The maximum number of times we allow a job to have itself rescheduled before
317 * giving up on it, for standard jobs.
318 */
319 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
320 /**
321 * The maximum number of times we allow a job to have itself rescheduled before
322 * giving up on it, for jobs that are executing work.
323 */
324 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
325 /**
326 * The minimum backoff time to allow for linear backoff.
327 */
328 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
329 /**
330 * The minimum backoff time to allow for exponential backoff.
331 */
332 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700333
334 private ContentResolver mResolver;
335 private final KeyValueListParser mParser = new KeyValueListParser(',');
336
337 public Constants(Handler handler) {
338 super(handler);
339 }
340
341 public void start(ContentResolver resolver) {
342 mResolver = resolver;
343 mResolver.registerContentObserver(Settings.Global.getUriFor(
344 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
345 updateConstants();
346 }
347
348 @Override
349 public void onChange(boolean selfChange, Uri uri) {
350 updateConstants();
351 }
352
353 private void updateConstants() {
354 synchronized (mLock) {
355 try {
356 mParser.setString(Settings.Global.getString(mResolver,
357 Settings.Global.ALARM_MANAGER_CONSTANTS));
358 } catch (IllegalArgumentException e) {
359 // Failed to parse the settings string, log this and move on
360 // with defaults.
361 Slog.e(TAG, "Bad device idle settings", e);
362 }
363
364 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
365 DEFAULT_MIN_IDLE_COUNT);
366 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
367 DEFAULT_MIN_CHARGING_COUNT);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800368 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
369 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700370 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
371 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700372 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
373 DEFAULT_MIN_CONNECTIVITY_COUNT);
374 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
375 DEFAULT_MIN_CONTENT_COUNT);
376 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
377 DEFAULT_MIN_READY_JOBS_COUNT);
378 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
379 DEFAULT_HEAVY_USE_FACTOR);
380 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
381 DEFAULT_MODERATE_USE_FACTOR);
382 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
383 DEFAULT_FG_JOB_COUNT);
384 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
385 DEFAULT_BG_NORMAL_JOB_COUNT);
386 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
387 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
388 }
389 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
390 DEFAULT_BG_MODERATE_JOB_COUNT);
391 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
392 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
393 }
394 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
395 DEFAULT_BG_LOW_JOB_COUNT);
396 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
397 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
398 }
399 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
400 DEFAULT_BG_CRITICAL_JOB_COUNT);
401 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
402 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
403 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700404 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
405 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
406 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
407 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
408 MIN_LINEAR_BACKOFF_TIME = mParser.getLong(KEY_MIN_LINEAR_BACKOFF_TIME,
409 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
410 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
411 DEFAULT_MIN_EXP_BACKOFF_TIME);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700412 }
413 }
414
415 void dump(PrintWriter pw) {
416 pw.println(" Settings:");
417
418 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
419 pw.print(MIN_IDLE_COUNT); pw.println();
420
421 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
422 pw.print(MIN_CHARGING_COUNT); pw.println();
423
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800424 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
425 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
426
Dianne Hackborn532ea262017-03-17 17:50:55 -0700427 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
428 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
429
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700430 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
431 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
432
433 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
434 pw.print(MIN_CONTENT_COUNT); pw.println();
435
436 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
437 pw.print(MIN_READY_JOBS_COUNT); pw.println();
438
439 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
440 pw.print(HEAVY_USE_FACTOR); pw.println();
441
442 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
443 pw.print(MODERATE_USE_FACTOR); pw.println();
444
445 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
446 pw.print(FG_JOB_COUNT); pw.println();
447
448 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
449 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
450
451 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
452 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
453
454 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
455 pw.print(BG_LOW_JOB_COUNT); pw.println();
456
457 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
458 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700459
460 pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
461 pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
462
463 pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
464 pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
465
466 pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
467 pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
468
469 pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
470 pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700471 }
472 }
473
474 final Constants mConstants;
475
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700476 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
477 if (o1.enqueueTime < o2.enqueueTime) {
478 return -1;
479 }
480 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
481 };
482
483 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
484 int where = Collections.binarySearch(array, newItem, comparator);
485 if (where < 0) {
486 where = ~where;
487 }
488 array.add(where, newItem);
489 }
490
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700491 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700492 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
493 * still clean up. On reinstall the package will have a new uid.
494 */
495 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
496 @Override
497 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700498 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700499 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700500 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700501 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700502 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700503 // Purge the app's jobs if the whole package was just disabled. When this is
504 // the case the component name will be a bare package name.
505 final String pkgName = getPackageName(intent);
506 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
507 if (pkgName != null && pkgUid != -1) {
508 final String[] changedComponents = intent.getStringArrayExtra(
509 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
510 if (changedComponents != null) {
511 for (String component : changedComponents) {
512 if (component.equals(pkgName)) {
513 if (DEBUG) {
514 Slog.d(TAG, "Package state change: " + pkgName);
515 }
516 try {
517 final int userId = UserHandle.getUserId(pkgUid);
518 IPackageManager pm = AppGlobals.getPackageManager();
519 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
520 if (state == COMPONENT_ENABLED_STATE_DISABLED
521 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
522 if (DEBUG) {
523 Slog.d(TAG, "Removing jobs for package " + pkgName
524 + " in user " + userId);
525 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800526 cancelJobsForUid(pkgUid);
Christopher Tateb5c07882016-05-26 17:11:09 -0700527 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700528 } catch (RemoteException|IllegalArgumentException e) {
529 /*
530 * IllegalArgumentException means that the package doesn't exist.
531 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
532 * behind outright uninstall, so by the time we try to act it's gone.
533 * We don't need to act on this PACKAGE_CHANGED when this happens;
534 * we'll get a PACKAGE_REMOVED later and clean up then.
535 *
536 * RemoteException can't actually happen; the package manager is
537 * running in this same process.
538 */
539 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700540 break;
541 }
542 }
543 }
544 } else {
545 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
546 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700547 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700548 // If this is an outright uninstall rather than the first half of an
549 // app update sequence, cancel the jobs associated with the app.
550 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
551 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
552 if (DEBUG) {
553 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
554 }
Dianne Hackborne07641d2016-11-09 15:07:23 -0800555 cancelJobsForUid(uidRemoved);
Christopher Tate7060b042014-06-09 19:50:00 -0700556 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700557 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700558 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
559 if (DEBUG) {
560 Slog.d(TAG, "Removing jobs for user: " + userId);
561 }
562 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700563 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
564 // Has this package scheduled any jobs, such that we will take action
565 // if it were to be force-stopped?
566 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
567 final String pkgName = intent.getData().getSchemeSpecificPart();
568 if (pkgUid != -1) {
569 List<JobStatus> jobsForUid;
570 synchronized (mLock) {
571 jobsForUid = mJobs.getJobsByUid(pkgUid);
572 }
573 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
574 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
575 if (DEBUG) {
576 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
577 + pkgUid + " has jobs");
578 }
579 setResultCode(Activity.RESULT_OK);
580 break;
581 }
582 }
583 }
584 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
585 // possible force-stop
586 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
587 final String pkgName = intent.getData().getSchemeSpecificPart();
588 if (pkgUid != -1) {
589 if (DEBUG) {
590 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
591 }
592 cancelJobsForPackageAndUid(pkgName, pkgUid);
593 }
Christopher Tate7060b042014-06-09 19:50:00 -0700594 }
595 }
596 };
597
Christopher Tateb5c07882016-05-26 17:11:09 -0700598 private String getPackageName(Intent intent) {
599 Uri uri = intent.getData();
600 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
601 return pkg;
602 }
603
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700604 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Sudheer Shanka80255802017-03-04 14:48:53 -0800605 @Override public void onUidStateChanged(int uid, int procState,
606 long procStateSeq) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800607 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700608 }
609
Dianne Hackborne07641d2016-11-09 15:07:23 -0800610 @Override public void onUidGone(int uid, boolean disabled) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800611 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800612 if (disabled) {
613 cancelJobsForUid(uid);
614 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700615 }
616
617 @Override public void onUidActive(int uid) throws RemoteException {
618 }
619
Dianne Hackborne07641d2016-11-09 15:07:23 -0800620 @Override public void onUidIdle(int uid, boolean disabled) throws RemoteException {
621 if (disabled) {
622 cancelJobsForUid(uid);
623 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700624 }
625 };
626
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800627 public Object getLock() {
628 return mLock;
629 }
630
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700631 public JobStore getJobStore() {
632 return mJobs;
633 }
634
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700635 @Override
636 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700637 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
638 // Let's kick any outstanding jobs for this user.
639 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
640 }
641
642 @Override
643 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700644 // Let's kick any outstanding jobs for this user.
645 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
646 }
647
648 @Override
649 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700650 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700651 }
652
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700653 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
654 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700655 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800656 if (ActivityManager.getService().isAppStartModeDisabled(uId,
657 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700658 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
659 + " -- package not allowed to start");
660 return JobScheduler.RESULT_FAILURE;
661 }
662 } catch (RemoteException e) {
663 }
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800664 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700665 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
666
667 if (work != null && toCancel != null) {
668 // Fast path: we are adding work to an existing job, and the JobInfo is not
669 // changing. We can just directly enqueue this work in to the job.
670 if (toCancel.getJob().equals(job)) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700671 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700672 return JobScheduler.RESULT_SUCCESS;
673 }
674 }
675
676 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
677 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800678 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800679 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800680 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
681 Slog.w(TAG, "Too many jobs for uid " + uId);
682 throw new IllegalStateException("Apps may not schedule more than "
683 + MAX_JOBS_PER_APP + " distinct jobs");
684 }
685 }
686
Dianne Hackborna47223f2017-03-30 13:49:13 -0700687 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700688 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700689
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700690 if (toCancel != null) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700691 cancelJobImplLocked(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700692 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700693 if (work != null) {
694 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700695 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700696 }
697 startTrackingJobLocked(jobStatus, toCancel);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700698
699 // If the job is immediately ready to run, then we can just immediately
700 // put it in the pending list and try to schedule it. This is especially
701 // important for jobs with a 0 deadline constraint, since they will happen a fair
702 // amount, we want to handle them as quickly as possible, and semantically we want to
703 // make sure we have started holding the wake lock for the job before returning to
704 // the caller.
705 // If the job is not yet ready to run, there is nothing more to do -- we are
706 // now just waiting for one of its controllers to change state and schedule
707 // the job appropriately.
708 if (isReadyToBeExecutedLocked(jobStatus)) {
709 // This is a new job, we can just immediately put it on the pending
710 // list and try to run it.
711 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700712 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700713 maybeRunPendingJobsLocked();
714 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800715 }
Christopher Tate7060b042014-06-09 19:50:00 -0700716 return JobScheduler.RESULT_SUCCESS;
717 }
718
719 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800720 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800721 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
722 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
723 for (int i = jobs.size() - 1; i >= 0; i--) {
724 JobStatus job = jobs.get(i);
725 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700726 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800727 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700728 }
Christopher Tate7060b042014-06-09 19:50:00 -0700729 }
730
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600731 public JobInfo getPendingJob(int uid, int jobId) {
732 synchronized (mLock) {
733 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
734 for (int i = jobs.size() - 1; i >= 0; i--) {
735 JobStatus job = jobs.get(i);
736 if (job.getJobId() == jobId) {
737 return job.getJob();
738 }
739 }
740 return null;
741 }
742 }
743
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700744 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800745 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700746 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
747 for (int i=0; i<jobsForUser.size(); i++) {
748 JobStatus toRemove = jobsForUser.get(i);
749 cancelJobImplLocked(toRemove, null);
750 }
Christopher Tate7060b042014-06-09 19:50:00 -0700751 }
752 }
753
Christopher Tateee7805b2016-07-15 16:56:56 -0700754 void cancelJobsForPackageAndUid(String pkgName, int uid) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700755 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700756 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
757 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
758 final JobStatus job = jobsForUid.get(i);
759 if (job.getSourcePackageName().equals(pkgName)) {
760 cancelJobImplLocked(job, null);
761 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700762 }
763 }
764 }
765
Christopher Tate7060b042014-06-09 19:50:00 -0700766 /**
767 * Entry point from client to cancel all jobs originating from their uid.
768 * This will remove the job from the master list, and cancel the job if it was staged for
769 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700770 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800771 *
Christopher Tate7060b042014-06-09 19:50:00 -0700772 */
Dianne Hackborne07641d2016-11-09 15:07:23 -0800773 public void cancelJobsForUid(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800774 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700775 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
776 for (int i=0; i<jobsForUid.size(); i++) {
777 JobStatus toRemove = jobsForUid.get(i);
778 cancelJobImplLocked(toRemove, null);
779 }
Christopher Tate7060b042014-06-09 19:50:00 -0700780 }
781 }
782
783 /**
784 * Entry point from client to cancel the job corresponding to the jobId provided.
785 * This will remove the job from the master list, and cancel the job if it was staged for
786 * execution or being executed.
787 * @param uid Uid of the calling client.
788 * @param jobId Id of the job, provided at schedule-time.
789 */
790 public void cancelJob(int uid, int jobId) {
791 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800792 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700793 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700794 if (toCancel != null) {
795 cancelJobImplLocked(toCancel, null);
796 }
Christopher Tate7060b042014-06-09 19:50:00 -0700797 }
798 }
799
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700800 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob) {
801 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
802 cancelled.unprepareLocked(ActivityManager.getService());
803 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
804 // Remove from pending queue.
805 if (mPendingJobs.remove(cancelled)) {
806 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -0700807 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700808 // Cancel if running.
809 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
810 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700811 }
812
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800813 void updateUidState(int uid, int procState) {
814 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800815 if (procState == ActivityManager.PROCESS_STATE_TOP) {
816 // Only use this if we are exactly the top app. All others can live
817 // with just the foreground priority. This means that persistent processes
818 // can never be the top app priority... that is fine.
819 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
820 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
821 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800822 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800823 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800824 }
825 }
826 }
827
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700828 @Override
829 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800830 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700831 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -0600832 // When becoming idle, make sure no jobs are actively running,
833 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700834 for (int i=0; i<mActiveServices.size(); i++) {
835 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700836 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -0600837 if (executing != null
838 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700839 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE);
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700840 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700841 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700842 } else {
843 // When coming out of idle, allow thing to start back up.
844 if (mReadyToRock) {
845 if (mLocalDeviceIdleController != null) {
846 if (!mReportedActive) {
847 mReportedActive = true;
848 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700849 }
850 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700851 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700852 }
853 }
854 }
855 }
856
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700857 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000858 // active is true if pending queue contains jobs OR some job is running.
859 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800860 if (mPendingJobs.size() <= 0) {
861 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700862 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700863 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700864 if (job != null
865 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
866 && !job.dozeWhitelisted) {
867 // We will report active if we have a job running and it is not an exception
868 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800869 active = true;
870 break;
871 }
872 }
873 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000874
875 if (mReportedActive != active) {
876 mReportedActive = active;
877 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800878 mLocalDeviceIdleController.setJobsActive(active);
879 }
880 }
881 }
882
Christopher Tate7060b042014-06-09 19:50:00 -0700883 /**
884 * Initializes the system service.
885 * <p>
886 * Subclasses must define a single argument constructor that accepts the context
887 * and passes it to super.
888 * </p>
889 *
890 * @param context The system server context.
891 */
892 public JobSchedulerService(Context context) {
893 super(context);
Dianne Hackborn970e3f42016-06-01 10:55:13 -0700894 mHandler = new JobHandler(context.getMainLooper());
895 mConstants = new Constants(mHandler);
896 mJobSchedulerStub = new JobSchedulerStub();
897 mJobs = JobStore.initAndGet(this);
898
Christopher Tate7060b042014-06-09 19:50:00 -0700899 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700900 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700901 mControllers.add(ConnectivityController.get(this));
902 mControllers.add(TimeController.get(this));
903 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800904 mBatteryController = BatteryController.get(this);
905 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700906 mStorageController = StorageController.get(this);
907 mControllers.add(mStorageController);
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800908 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800909 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700910 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700911 }
912
913 @Override
914 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000915 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700916 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
917 }
918
919 @Override
920 public void onBootPhase(int phase) {
921 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700922 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000923 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700924 final IntentFilter filter = new IntentFilter();
925 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
926 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -0700927 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
928 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -0700929 filter.addDataScheme("package");
930 getContext().registerReceiverAsUser(
931 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
932 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
933 getContext().registerReceiverAsUser(
934 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000935 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700936 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800937 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800938 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Dianne Hackborn5614bf52016-11-07 17:26:41 -0800939 | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
940 null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700941 } catch (RemoteException e) {
942 // ignored; both services live in system_server
943 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700944 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800945 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700946 // Let's go!
947 mReadyToRock = true;
948 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
949 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800950 mLocalDeviceIdleController
951 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700952 // Create the "runners".
953 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
954 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700955 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700956 getContext().getMainLooper()));
957 }
958 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800959 mJobs.forEachJob(new JobStatusFunctor() {
960 @Override
961 public void process(JobStatus job) {
962 for (int controller = 0; controller < mControllers.size(); controller++) {
963 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800964 sc.maybeStartTrackingJobLocked(job, null);
965 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700966 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800967 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700968 // GO GO GO!
969 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
970 }
Christopher Tate7060b042014-06-09 19:50:00 -0700971 }
972 }
973
974 /**
975 * Called when we have a job status object that we need to insert in our
976 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
977 * about.
978 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700979 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
980 if (!jobStatus.isPreparedLocked()) {
981 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
982 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700983 jobStatus.enqueueTime = SystemClock.elapsedRealtime();
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700984 final boolean update = mJobs.add(jobStatus);
985 if (mReadyToRock) {
986 for (int i = 0; i < mControllers.size(); i++) {
987 StateController controller = mControllers.get(i);
988 if (update) {
989 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700990 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700991 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -0700992 }
Christopher Tate7060b042014-06-09 19:50:00 -0700993 }
994 }
995
996 /**
997 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
998 * object removed.
999 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001000 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001001 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001002 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001003 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001004
1005 // Remove from store as well as controllers.
1006 final boolean removed = mJobs.remove(jobStatus, writeBack);
1007 if (removed && mReadyToRock) {
1008 for (int i=0; i<mControllers.size(); i++) {
1009 StateController controller = mControllers.get(i);
1010 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001011 }
1012 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001013 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001014 }
1015
Shreyas Basarge5db09082016-01-07 13:38:29 +00001016 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001017 for (int i=0; i<mActiveServices.size(); i++) {
1018 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001019 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001020 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn342e6032017-04-13 18:04:31 -07001021 jsc.cancelExecutingJobLocked(reason);
Christopher Tate7060b042014-06-09 19:50:00 -07001022 return true;
1023 }
1024 }
1025 return false;
1026 }
1027
1028 /**
1029 * @param job JobStatus we are querying against.
1030 * @return Whether or not the job represented by the status object is currently being run or
1031 * is pending.
1032 */
1033 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001034 for (int i=0; i<mActiveServices.size(); i++) {
1035 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001036 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001037 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1038 return true;
1039 }
1040 }
1041 return false;
1042 }
1043
Dianne Hackborn807de782016-04-07 17:54:41 -07001044 void noteJobsPending(List<JobStatus> jobs) {
1045 for (int i = jobs.size() - 1; i >= 0; i--) {
1046 JobStatus job = jobs.get(i);
1047 mJobPackageTracker.notePending(job);
1048 }
1049 }
1050
1051 void noteJobsNonpending(List<JobStatus> jobs) {
1052 for (int i = jobs.size() - 1; i >= 0; i--) {
1053 JobStatus job = jobs.get(i);
1054 mJobPackageTracker.noteNonpending(job);
1055 }
1056 }
1057
Christopher Tate7060b042014-06-09 19:50:00 -07001058 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001059 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1060 * specify an override deadline on a failed job (the failed job will run even though it's not
1061 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1062 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1063 *
Christopher Tate7060b042014-06-09 19:50:00 -07001064 * @param failureToReschedule Provided job status that we will reschedule.
1065 * @return A newly instantiated JobStatus with the same constraints as the last job except
1066 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001067 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001068 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001069 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001070 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001071 final long elapsedNowMillis = SystemClock.elapsedRealtime();
1072 final JobInfo job = failureToReschedule.getJob();
1073
1074 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001075 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1076 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001077
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001078 if (failureToReschedule.hasWorkLocked()) {
1079 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1080 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1081 + backoffAttempts + " > work limit "
1082 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1083 return null;
1084 }
1085 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1086 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1087 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1088 return null;
1089 }
1090
Christopher Tate7060b042014-06-09 19:50:00 -07001091 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001092 case JobInfo.BACKOFF_POLICY_LINEAR: {
1093 long backoff = initialBackoffMillis;
1094 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1095 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1096 }
1097 delayMillis = backoff * backoffAttempts;
1098 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001099 default:
1100 if (DEBUG) {
1101 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1102 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001103 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1104 long backoff = initialBackoffMillis;
1105 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1106 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1107 }
1108 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1109 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001110 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001111 delayMillis =
1112 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001113 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -07001114 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001115 for (int ic=0; ic<mControllers.size(); ic++) {
1116 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001117 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001118 }
1119 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001120 }
1121
1122 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001123 * Called after a periodic has executed so we can reschedule it. We take the last execution
1124 * time of the job to be the time of completion (i.e. the time at which this function is
1125 * called).
Christopher Tate7060b042014-06-09 19:50:00 -07001126 * This could be inaccurate b/c the job can run for as long as
1127 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1128 * to underscheduling at least, rather than if we had taken the last execution time to be the
1129 * start of the execution.
1130 * @return A new job representing the execution criteria for this instantiation of the
1131 * recurring job.
1132 */
1133 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
1134 final long elapsedNow = SystemClock.elapsedRealtime();
1135 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001136 long runEarly = 0L;
1137
1138 // If this periodic was rescheduled it won't have a deadline.
1139 if (periodicToReschedule.hasDeadlineConstraint()) {
1140 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1141 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001142 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001143 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001144 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1145 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001146
1147 if (DEBUG) {
1148 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1149 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1150 }
1151 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
1152 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
1153 }
1154
1155 // JobCompletedListener implementations.
1156
1157 /**
1158 * A job just finished executing. We fetch the
1159 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1160 * whether we want to reschedule we readd it to the controllers.
1161 * @param jobStatus Completed job.
1162 * @param needsReschedule Whether the implementing class should reschedule this job.
1163 */
1164 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001165 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001166 if (DEBUG) {
1167 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1168 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001169
1170 // If the job wants to be rescheduled, we first need to make the next upcoming
1171 // job so we can transfer any appropriate state over from the previous job when
1172 // we stop it.
1173 final JobStatus rescheduledJob = needsReschedule
1174 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1175
Shreyas Basarge73f10252016-02-11 17:06:13 +00001176 // Do not write back immediately if this is a periodic job. The job may get lost if system
1177 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001178 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001179 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001180 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001181 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001182 // We still want to check for jobs to execute, because this job may have
1183 // scheduled a new job under the same job id, and now we can run it.
1184 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001185 return;
1186 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001187
1188 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001189 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001190 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001191 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001192 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001193 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001194 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001195 } else if (jobStatus.getJob().isPeriodic()) {
1196 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001197 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001198 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001199 } catch (SecurityException e) {
1200 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1201 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001202 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001203 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001204 jobStatus.unprepareLocked(ActivityManager.getService());
1205 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001206 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001207 }
1208
1209 // StateChangedListener implementations.
1210
1211 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001212 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1213 * some controller's state has changed, so as to run through the list of jobs and start/stop
1214 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001215 */
1216 @Override
1217 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001218 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001219 }
1220
1221 @Override
1222 public void onRunJobNow(JobStatus jobStatus) {
1223 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1224 }
1225
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001226 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001227
1228 public JobHandler(Looper looper) {
1229 super(looper);
1230 }
1231
1232 @Override
1233 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001234 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001235 if (!mReadyToRock) {
1236 return;
1237 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001238 switch (message.what) {
1239 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001240 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001241 // runNow can be null, which is a controller's way of indicating that its
1242 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001243 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001244 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001245 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001246 } else {
1247 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001248 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001249 } break;
1250 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001251 if (mReportedActive) {
1252 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001253 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001254 } else {
1255 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001256 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001257 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001258 break;
1259 case MSG_CHECK_JOB_GREEDY:
1260 queueReadyJobsForExecutionLocked();
1261 break;
1262 case MSG_STOP_JOB:
1263 cancelJobImplLocked((JobStatus) message.obj, null);
1264 break;
Matthew Williams75fc5252014-09-02 16:17:53 -07001265 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001266 maybeRunPendingJobsLocked();
1267 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1268 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001269 }
1270 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001271 }
Christopher Tate7060b042014-06-09 19:50:00 -07001272
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001273 /**
1274 * Run through list of jobs and execute all possible - at least one is expired so we do
1275 * as many as we can.
1276 */
1277 private void queueReadyJobsForExecutionLocked() {
1278 if (DEBUG) {
1279 Slog.d(TAG, "queuing all ready jobs for execution:");
1280 }
1281 noteJobsNonpending(mPendingJobs);
1282 mPendingJobs.clear();
1283 mJobs.forEachJob(mReadyQueueFunctor);
1284 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001285
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001286 if (DEBUG) {
1287 final int queuedJobs = mPendingJobs.size();
1288 if (queuedJobs == 0) {
1289 Slog.d(TAG, "No jobs pending.");
1290 } else {
1291 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001292 }
1293 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001294 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001295
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001296 final class ReadyJobQueueFunctor implements JobStatusFunctor {
1297 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001298
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001299 @Override
1300 public void process(JobStatus job) {
1301 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001302 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001303 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001304 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001305 if (newReadyJobs == null) {
1306 newReadyJobs = new ArrayList<JobStatus>();
1307 }
1308 newReadyJobs.add(job);
1309 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1310 stopJobOnServiceContextLocked(job,
1311 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001312 }
1313 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001314
1315 public void postProcess() {
1316 if (newReadyJobs != null) {
1317 noteJobsPending(newReadyJobs);
1318 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001319 if (mPendingJobs.size() > 1) {
1320 mPendingJobs.sort(mEnqueueTimeComparator);
1321 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001322 }
1323 newReadyJobs = null;
1324 }
1325 }
1326 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1327
1328 /**
1329 * The state of at least one job has changed. Here is where we could enforce various
1330 * policies on when we want to execute jobs.
1331 * Right now the policy is such:
1332 * If >1 of the ready jobs is idle mode we send all of them off
1333 * if more than 2 network connectivity jobs are ready we send them all off.
1334 * If more than 4 jobs total are ready we send them all off.
1335 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1336 */
1337 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1338 int chargingCount;
1339 int batteryNotLowCount;
1340 int storageNotLowCount;
1341 int idleCount;
1342 int backoffCount;
1343 int connectivityCount;
1344 int contentCount;
1345 List<JobStatus> runnableJobs;
1346
1347 public MaybeReadyJobQueueFunctor() {
1348 reset();
1349 }
1350
1351 // Functor method invoked for each job via JobStore.forEachJob()
1352 @Override
1353 public void process(JobStatus job) {
1354 if (isReadyToBeExecutedLocked(job)) {
1355 try {
1356 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1357 job.getJob().getService().getPackageName())) {
1358 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1359 + job.getJob().toString() + " -- package not allowed to start");
1360 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1361 return;
1362 }
1363 } catch (RemoteException e) {
1364 }
1365 if (job.getNumFailures() > 0) {
1366 backoffCount++;
1367 }
1368 if (job.hasIdleConstraint()) {
1369 idleCount++;
1370 }
1371 if (job.hasConnectivityConstraint()) {
1372 connectivityCount++;
1373 }
1374 if (job.hasChargingConstraint()) {
1375 chargingCount++;
1376 }
1377 if (job.hasBatteryNotLowConstraint()) {
1378 batteryNotLowCount++;
1379 }
1380 if (job.hasStorageNotLowConstraint()) {
1381 storageNotLowCount++;
1382 }
1383 if (job.hasContentTriggerConstraint()) {
1384 contentCount++;
1385 }
1386 if (runnableJobs == null) {
1387 runnableJobs = new ArrayList<>();
1388 }
1389 runnableJobs.add(job);
1390 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1391 stopJobOnServiceContextLocked(job,
1392 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1393 }
1394 }
1395
1396 public void postProcess() {
1397 if (backoffCount > 0 ||
1398 idleCount >= mConstants.MIN_IDLE_COUNT ||
1399 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1400 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1401 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1402 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1403 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1404 (runnableJobs != null
1405 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1406 if (DEBUG) {
1407 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1408 }
1409 noteJobsPending(runnableJobs);
1410 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001411 if (mPendingJobs.size() > 1) {
1412 mPendingJobs.sort(mEnqueueTimeComparator);
1413 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001414 } else {
1415 if (DEBUG) {
1416 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1417 }
1418 }
1419
1420 // Be ready for next time
1421 reset();
1422 }
1423
1424 private void reset() {
1425 chargingCount = 0;
1426 idleCount = 0;
1427 backoffCount = 0;
1428 connectivityCount = 0;
1429 batteryNotLowCount = 0;
1430 storageNotLowCount = 0;
1431 contentCount = 0;
1432 runnableJobs = null;
1433 }
1434 }
1435 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1436
1437 private void maybeQueueReadyJobsForExecutionLocked() {
1438 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1439
1440 noteJobsNonpending(mPendingJobs);
1441 mPendingJobs.clear();
1442 mJobs.forEachJob(mMaybeQueueFunctor);
1443 mMaybeQueueFunctor.postProcess();
1444 }
1445
1446 /**
1447 * Criteria for moving a job into the pending queue:
1448 * - It's ready.
1449 * - It's not pending.
1450 * - It's not already running on a JSC.
1451 * - The user that requested the job is running.
1452 * - The component is enabled and runnable.
1453 */
1454 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001455 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001456
1457 if (DEBUG) {
1458 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1459 + " ready=" + jobReady);
1460 }
1461
1462 // This is a condition that is very likely to be false (most jobs that are
1463 // scheduled are sitting there, not ready yet) and very cheap to check (just
1464 // a few conditions on data in JobStatus).
1465 if (!jobReady) {
1466 return false;
1467 }
1468
1469 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001470
1471 final int userId = job.getUserId();
1472 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1473
1474 if (DEBUG) {
1475 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001476 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001477 }
1478
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001479 // These are also fairly cheap to check, though they typically will not
1480 // be conditions we fail.
1481 if (!jobExists || !userStarted) {
1482 return false;
1483 }
1484
1485 final boolean jobPending = mPendingJobs.contains(job);
1486 final boolean jobActive = isCurrentlyActiveLocked(job);
1487
1488 if (DEBUG) {
1489 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1490 + " pending=" + jobPending + " active=" + jobActive);
1491 }
1492
1493 // These can be a little more expensive (especially jobActive, since we need to
1494 // go through the array of all potentially active jobs), so we are doing them
1495 // later... but still before checking with the package manager!
1496 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001497 return false;
1498 }
1499
1500 final boolean componentPresent;
1501 try {
1502 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1503 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1504 userId) != null);
1505 } catch (RemoteException e) {
1506 throw e.rethrowAsRuntimeException();
1507 }
1508
1509 if (DEBUG) {
1510 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1511 + " componentPresent=" + componentPresent);
1512 }
1513
1514 // Everything else checked out so far, so this is the final yes/no check
1515 return componentPresent;
1516 }
1517
1518 /**
1519 * Criteria for cancelling an active job:
1520 * - It's not ready
1521 * - It's running on a JSC.
1522 */
1523 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
1524 return !job.isReady() && isCurrentlyActiveLocked(job);
1525 }
1526
1527 /**
1528 * Reconcile jobs in the pending queue against available execution contexts.
1529 * A controller can force a job into the pending queue even if it's already running, but
1530 * here is where we decide whether to actually execute it.
1531 */
1532 private void maybeRunPendingJobsLocked() {
1533 if (DEBUG) {
1534 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1535 }
1536 assignJobsToContextsLocked();
1537 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001538 }
1539
Dianne Hackborn807de782016-04-07 17:54:41 -07001540 private int adjustJobPriority(int curPriority, JobStatus job) {
1541 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1542 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001543 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001544 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001545 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001546 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1547 }
1548 }
1549 return curPriority;
1550 }
1551
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001552 private int evaluateJobPriorityLocked(JobStatus job) {
1553 int priority = job.getPriority();
1554 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001555 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001556 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001557 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1558 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001559 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001560 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001561 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001562 }
1563
Christopher Tate7060b042014-06-09 19:50:00 -07001564 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001565 * Takes jobs from pending queue and runs them on available contexts.
1566 * If no contexts are available, preempts lower priority jobs to
1567 * run higher priority ones.
1568 * Lock on mJobs before calling this function.
1569 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001570 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001571 if (DEBUG) {
1572 Slog.d(TAG, printPendingQueue());
1573 }
1574
Dianne Hackborn970510b2016-02-24 16:56:42 -08001575 int memLevel;
1576 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001577 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001578 } catch (RemoteException e) {
1579 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1580 }
1581 switch (memLevel) {
1582 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001583 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001584 break;
1585 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001586 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001587 break;
1588 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001589 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001590 break;
1591 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001592 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001593 break;
1594 }
1595
1596 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1597 boolean[] act = mTmpAssignAct;
1598 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1599 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001600 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001601 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1602 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001603 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001604 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001605 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001606 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1607 numForeground++;
1608 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001609 }
1610 act[i] = false;
1611 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001612 }
1613 if (DEBUG) {
1614 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1615 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001616 for (int i=0; i<mPendingJobs.size(); i++) {
1617 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001618
1619 // If job is already running, go to next job.
1620 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1621 if (jobRunningContext != -1) {
1622 continue;
1623 }
1624
Dianne Hackborn970510b2016-02-24 16:56:42 -08001625 final int priority = evaluateJobPriorityLocked(nextPending);
1626 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001627
Shreyas Basarge5db09082016-01-07 13:38:29 +00001628 // Find a context for nextPending. The context should be available OR
1629 // it should have lowest priority among all running jobs
1630 // (sharing the same Uid as nextPending)
1631 int minPriority = Integer.MAX_VALUE;
1632 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001633 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1634 JobStatus job = contextIdToJobMap[j];
1635 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001636 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001637 if ((numActive < mMaxActiveJobs ||
1638 (priority >= JobInfo.PRIORITY_TOP_APP &&
1639 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001640 (preferredUid == nextPending.getUid() ||
1641 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1642 // This slot is free, and we haven't yet hit the limit on
1643 // concurrent jobs... we can just throw the job in to here.
1644 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001645 break;
1646 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001647 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001648 // the context has a preferred Uid or we have reached the limit on
1649 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001650 continue;
1651 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001652 if (job.getUid() != nextPending.getUid()) {
1653 continue;
1654 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001655 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001656 continue;
1657 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001658 if (minPriority > nextPending.lastEvaluatedPriority) {
1659 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001660 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001661 }
1662 }
1663 if (minPriorityContextId != -1) {
1664 contextIdToJobMap[minPriorityContextId] = nextPending;
1665 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001666 numActive++;
1667 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1668 numForeground++;
1669 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001670 }
1671 }
1672 if (DEBUG) {
1673 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1674 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001675 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001676 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001677 boolean preservePreferredUid = false;
1678 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001679 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001680 if (js != null) {
1681 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001682 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00001683 }
1684 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001685 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001686 preservePreferredUid = true;
1687 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001688 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001689 if (DEBUG) {
1690 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001691 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001692 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001693 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001694 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001695 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001696 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1697 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001698 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001699 if (mPendingJobs.remove(pendingJob)) {
1700 mJobPackageTracker.noteNonpending(pendingJob);
1701 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001702 }
1703 }
1704 if (!preservePreferredUid) {
1705 mActiveServices.get(i).clearPreferredUid();
1706 }
1707 }
1708 }
1709
1710 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1711 for (int i=0; i<map.length; i++) {
1712 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1713 return i;
1714 }
1715 }
1716 return -1;
1717 }
1718
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001719 final class LocalService implements JobSchedulerInternal {
1720
1721 /**
1722 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1723 * jobs are always considered pending.
1724 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001725 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001726 public List<JobInfo> getSystemScheduledPendingJobs() {
1727 synchronized (mLock) {
1728 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1729 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1730 @Override
1731 public void process(JobStatus job) {
1732 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1733 pendingJobs.add(job.getJob());
1734 }
1735 }
1736 });
1737 return pendingJobs;
1738 }
1739 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001740
1741 @Override
1742 public void addBackingUpUid(int uid) {
1743 synchronized (mLock) {
1744 // No need to actually do anything here, since for a full backup the
1745 // activity manager will kill the process which will kill the job (and
1746 // cause it to restart, but now it can't run).
1747 mBackingUpUids.put(uid, uid);
1748 }
1749 }
1750
1751 @Override
1752 public void removeBackingUpUid(int uid) {
1753 synchronized (mLock) {
1754 mBackingUpUids.delete(uid);
1755 // If there are any jobs for this uid, we need to rebuild the pending list
1756 // in case they are now ready to run.
1757 if (mJobs.countJobsForUid(uid) > 0) {
1758 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1759 }
1760 }
1761 }
1762
1763 @Override
1764 public void clearAllBackingUpUids() {
1765 synchronized (mLock) {
1766 if (mBackingUpUids.size() > 0) {
1767 mBackingUpUids.clear();
1768 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1769 }
1770 }
1771 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001772 }
1773
Shreyas Basarge5db09082016-01-07 13:38:29 +00001774 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001775 * Binder stub trampoline implementation
1776 */
1777 final class JobSchedulerStub extends IJobScheduler.Stub {
1778 /** Cache determination of whether a given app can persist jobs
1779 * key is uid of the calling app; value is undetermined/true/false
1780 */
1781 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1782
1783 // Enforce that only the app itself (or shared uid participant) can schedule a
1784 // job that runs one of the app's services, as well as verifying that the
1785 // named service properly requires the BIND_JOB_SERVICE permission
1786 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001787 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001788 final ComponentName service = job.getService();
1789 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001790 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001791 PackageManager.MATCH_DIRECT_BOOT_AWARE
1792 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001793 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001794 if (si == null) {
1795 throw new IllegalArgumentException("No such service " + service);
1796 }
Christopher Tate7060b042014-06-09 19:50:00 -07001797 if (si.applicationInfo.uid != uid) {
1798 throw new IllegalArgumentException("uid " + uid +
1799 " cannot schedule job in " + service.getPackageName());
1800 }
1801 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1802 throw new IllegalArgumentException("Scheduled service " + service
1803 + " does not require android.permission.BIND_JOB_SERVICE permission");
1804 }
Christopher Tate5568f542014-06-18 13:53:31 -07001805 } catch (RemoteException e) {
1806 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001807 }
1808 }
1809
1810 private boolean canPersistJobs(int pid, int uid) {
1811 // If we get this far we're good to go; all we need to do now is check
1812 // whether the app is allowed to persist its scheduled work.
1813 final boolean canPersist;
1814 synchronized (mPersistCache) {
1815 Boolean cached = mPersistCache.get(uid);
1816 if (cached != null) {
1817 canPersist = cached.booleanValue();
1818 } else {
1819 // Persisting jobs is tantamount to running at boot, so we permit
1820 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1821 // permission
1822 int result = getContext().checkPermission(
1823 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1824 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1825 mPersistCache.put(uid, canPersist);
1826 }
1827 }
1828 return canPersist;
1829 }
1830
1831 // IJobScheduler implementation
1832 @Override
1833 public int schedule(JobInfo job) throws RemoteException {
1834 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001835 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001836 }
1837 final int pid = Binder.getCallingPid();
1838 final int uid = Binder.getCallingUid();
1839
1840 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001841 if (job.isPersisted()) {
1842 if (!canPersistJobs(pid, uid)) {
1843 throw new IllegalArgumentException("Error: requested job be persisted without"
1844 + " holding RECEIVE_BOOT_COMPLETED permission.");
1845 }
1846 }
Christopher Tate7060b042014-06-09 19:50:00 -07001847
Jeff Sharkey785f4942016-07-14 10:31:15 -06001848 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1849 getContext().enforceCallingOrSelfPermission(
1850 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1851 }
1852
Christopher Tate7060b042014-06-09 19:50:00 -07001853 long ident = Binder.clearCallingIdentity();
1854 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001855 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, -1, null);
1856 } finally {
1857 Binder.restoreCallingIdentity(ident);
1858 }
1859 }
1860
1861 // IJobScheduler implementation
1862 @Override
1863 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
1864 if (DEBUG) {
1865 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
1866 }
1867 final int pid = Binder.getCallingPid();
1868 final int uid = Binder.getCallingUid();
1869
1870 enforceValidJobRequest(uid, job);
1871 if (job.isPersisted()) {
1872 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
1873 }
1874 if (work == null) {
1875 throw new NullPointerException("work is null");
1876 }
1877
1878 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1879 getContext().enforceCallingOrSelfPermission(
1880 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1881 }
1882
1883 long ident = Binder.clearCallingIdentity();
1884 try {
1885 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, -1, null);
Christopher Tate7060b042014-06-09 19:50:00 -07001886 } finally {
1887 Binder.restoreCallingIdentity(ident);
1888 }
1889 }
1890
1891 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001892 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001893 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001894 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001895 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001896 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1897 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001898 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001899
1900 if (packageName == null) {
1901 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001902 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001903
1904 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1905 android.Manifest.permission.UPDATE_DEVICE_STATS);
1906 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1907 throw new SecurityException("Caller uid " + callerUid
1908 + " not permitted to schedule jobs for other apps");
1909 }
1910
Jeff Sharkey4f100402016-05-03 17:44:23 -06001911 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1912 getContext().enforceCallingOrSelfPermission(
1913 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1914 }
1915
Shreyas Basarge968ac752016-01-11 23:09:26 +00001916 long ident = Binder.clearCallingIdentity();
1917 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001918 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001919 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001920 } finally {
1921 Binder.restoreCallingIdentity(ident);
1922 }
1923 }
1924
1925 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001926 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1927 final int uid = Binder.getCallingUid();
1928
1929 long ident = Binder.clearCallingIdentity();
1930 try {
1931 return JobSchedulerService.this.getPendingJobs(uid);
1932 } finally {
1933 Binder.restoreCallingIdentity(ident);
1934 }
1935 }
1936
1937 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001938 public JobInfo getPendingJob(int jobId) throws RemoteException {
1939 final int uid = Binder.getCallingUid();
1940
1941 long ident = Binder.clearCallingIdentity();
1942 try {
1943 return JobSchedulerService.this.getPendingJob(uid, jobId);
1944 } finally {
1945 Binder.restoreCallingIdentity(ident);
1946 }
1947 }
1948
1949 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001950 public void cancelAll() throws RemoteException {
1951 final int uid = Binder.getCallingUid();
1952
1953 long ident = Binder.clearCallingIdentity();
1954 try {
Dianne Hackborne07641d2016-11-09 15:07:23 -08001955 JobSchedulerService.this.cancelJobsForUid(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001956 } finally {
1957 Binder.restoreCallingIdentity(ident);
1958 }
1959 }
1960
1961 @Override
1962 public void cancel(int jobId) throws RemoteException {
1963 final int uid = Binder.getCallingUid();
1964
1965 long ident = Binder.clearCallingIdentity();
1966 try {
1967 JobSchedulerService.this.cancelJob(uid, jobId);
1968 } finally {
1969 Binder.restoreCallingIdentity(ident);
1970 }
1971 }
1972
1973 /**
1974 * "dumpsys" infrastructure
1975 */
1976 @Override
1977 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06001978 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07001979
1980 long identityToken = Binder.clearCallingIdentity();
1981 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001982 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001983 } finally {
1984 Binder.restoreCallingIdentity(identityToken);
1985 }
1986 }
Christopher Tate5d346052016-03-08 12:56:08 -08001987
1988 @Override
1989 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07001990 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08001991 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07001992 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08001993 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001994 };
1995
Christopher Tate5d346052016-03-08 12:56:08 -08001996 // Shell command infrastructure: run the given job immediately
1997 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1998 if (DEBUG) {
1999 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2000 + " " + jobId + " f=" + force);
2001 }
2002
2003 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002004 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2005 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002006 if (uid < 0) {
2007 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2008 }
2009
2010 synchronized (mLock) {
2011 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2012 if (js == null) {
2013 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2014 }
2015
2016 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2017 if (!js.isConstraintsSatisfied()) {
2018 js.overrideState = 0;
2019 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2020 }
2021
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002022 queueReadyJobsForExecutionLocked();
2023 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002024 }
2025 } catch (RemoteException e) {
2026 // can't happen
2027 }
2028 return 0;
2029 }
2030
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002031 // Shell command infrastructure: immediately timeout currently executing jobs
2032 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2033 boolean hasJobId, int jobId) {
2034 if (DEBUG) {
2035 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2036 }
2037
2038 synchronized (mLock) {
2039 boolean foundSome = false;
2040 for (int i=0; i<mActiveServices.size(); i++) {
2041 mActiveServices.get(i).timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId);
2042 }
2043 if (!foundSome) {
2044 pw.println("No matching executing jobs found.");
2045 }
2046 }
2047 return 0;
2048 }
2049
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002050 void setMonitorBattery(boolean enabled) {
2051 synchronized (mLock) {
2052 if (mBatteryController != null) {
2053 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2054 }
2055 }
2056 }
2057
2058 int getBatterySeq() {
2059 synchronized (mLock) {
2060 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2061 }
2062 }
2063
2064 boolean getBatteryCharging() {
2065 synchronized (mLock) {
2066 return mBatteryController != null
2067 ? mBatteryController.getTracker().isOnStablePower() : false;
2068 }
2069 }
2070
2071 boolean getBatteryNotLow() {
2072 synchronized (mLock) {
2073 return mBatteryController != null
2074 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2075 }
2076 }
2077
Dianne Hackborn532ea262017-03-17 17:50:55 -07002078 int getStorageSeq() {
2079 synchronized (mLock) {
2080 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2081 }
2082 }
2083
2084 boolean getStorageNotLow() {
2085 synchronized (mLock) {
2086 return mStorageController != null
2087 ? mStorageController.getTracker().isStorageNotLow() : false;
2088 }
2089 }
2090
Shreyas Basarge5db09082016-01-07 13:38:29 +00002091 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2092 StringBuilder s = new StringBuilder(initial + ": ");
2093 for (int i=0; i<map.length; i++) {
2094 s.append("(")
2095 .append(map[i] == null? -1: map[i].getJobId())
2096 .append(map[i] == null? -1: map[i].getUid())
2097 .append(")" );
2098 }
2099 return s.toString();
2100 }
2101
2102 private String printPendingQueue() {
2103 StringBuilder s = new StringBuilder("Pending queue: ");
2104 Iterator<JobStatus> it = mPendingJobs.iterator();
2105 while (it.hasNext()) {
2106 JobStatus js = it.next();
2107 s.append("(")
2108 .append(js.getJob().getId())
2109 .append(", ")
2110 .append(js.getUid())
2111 .append(") ");
2112 }
2113 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07002114 }
Christopher Tate7060b042014-06-09 19:50:00 -07002115
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002116 static void dumpHelp(PrintWriter pw) {
2117 pw.println("Job Scheduler (jobscheduler) dump options:");
2118 pw.println(" [-h] [package] ...");
2119 pw.println(" -h: print this help");
2120 pw.println(" [package] is an optional package name to limit the output to.");
2121 }
2122
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002123 void dumpInternal(final PrintWriter pw, String[] args) {
2124 int filterUid = -1;
2125 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002126 int opti = 0;
2127 while (opti < args.length) {
2128 String arg = args[opti];
2129 if ("-h".equals(arg)) {
2130 dumpHelp(pw);
2131 return;
2132 } else if ("-a".equals(arg)) {
2133 // Ignore, we always dump all.
2134 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2135 pw.println("Unknown option: " + arg);
2136 return;
2137 } else {
2138 break;
2139 }
2140 opti++;
2141 }
2142 if (opti < args.length) {
2143 String pkg = args[opti];
2144 try {
2145 filterUid = getContext().getPackageManager().getPackageUid(pkg,
Amith Yamasani0d1fd8d2016-10-12 14:21:51 -07002146 PackageManager.MATCH_ANY_USER);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002147 } catch (NameNotFoundException ignored) {
2148 pw.println("Invalid package: " + pkg);
2149 return;
2150 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002151 }
2152 }
2153
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002154 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07002155 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002156 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002157 mConstants.dump(pw);
2158 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07002159 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002160 pw.print("Registered ");
2161 pw.print(mJobs.size());
2162 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07002163 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002164 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2165 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002166 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002167 public int compare(JobStatus o1, JobStatus o2) {
2168 int uid1 = o1.getUid();
2169 int uid2 = o2.getUid();
2170 int id1 = o1.getJobId();
2171 int id2 = o2.getJobId();
2172 if (uid1 != uid2) {
2173 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002174 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002175 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08002176 }
2177 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002178 for (JobStatus job : jobs) {
2179 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2180 pw.println(job.toShortStringExceptUniqueId());
2181
2182 // Skip printing details if the caller requested a filter
2183 if (!job.shouldDump(filterUidFinal)) {
2184 continue;
2185 }
2186
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002187 job.dump(pw, " ", true, now);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002188 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002189 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002190 pw.print(" (job=");
2191 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002192 pw.print(" user=");
2193 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002194 pw.print(" !pending=");
2195 pw.print(!mPendingJobs.contains(job));
2196 pw.print(" !active=");
2197 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002198 pw.print(" !backingup=");
2199 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002200 pw.print(" comp=");
2201 boolean componentPresent = false;
2202 try {
2203 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2204 job.getServiceComponent(),
2205 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2206 job.getUserId()) != null);
2207 } catch (RemoteException e) {
2208 }
2209 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002210 pw.println(")");
2211 }
Christopher Tate7060b042014-06-09 19:50:00 -07002212 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002213 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002214 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002215 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002216 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002217 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07002218 }
2219 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002220 pw.println("Uid priority overrides:");
2221 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002222 int uid = mUidPriorityOverride.keyAt(i);
2223 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2224 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2225 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2226 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002227 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002228 if (mBackingUpUids.size() > 0) {
2229 pw.println();
2230 pw.println("Backing up uids:");
2231 boolean first = true;
2232 for (int i = 0; i < mBackingUpUids.size(); i++) {
2233 int uid = mBackingUpUids.keyAt(i);
2234 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2235 if (first) {
2236 pw.print(" ");
2237 first = false;
2238 } else {
2239 pw.print(", ");
2240 }
2241 pw.print(UserHandle.formatUid(uid));
2242 }
2243 }
2244 pw.println();
2245 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002246 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002247 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002248 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002249 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2250 pw.println();
2251 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002252 pw.println("Pending queue:");
2253 for (int i=0; i<mPendingJobs.size(); i++) {
2254 JobStatus job = mPendingJobs.get(i);
2255 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2256 pw.println(job.toShortString());
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002257 job.dump(pw, " ", false, now);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002258 int priority = evaluateJobPriorityLocked(job);
2259 if (priority != JobInfo.PRIORITY_DEFAULT) {
2260 pw.print(" Evaluated priority: "); pw.println(priority);
2261 }
2262 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07002263 pw.print(" Enq: ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002264 TimeUtils.formatDuration(job.madePending - now, pw);
2265 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002266 }
Christopher Tate7060b042014-06-09 19:50:00 -07002267 pw.println();
2268 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002269 for (int i=0; i<mActiveServices.size(); i++) {
2270 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002271 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002272 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07002273 if (job == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002274 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07002275 continue;
2276 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07002277 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002278 pw.print(" Running for: ");
2279 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
2280 pw.print(", timeout at: ");
2281 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
2282 pw.println();
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002283 job.dump(pw, " ", false, now);
2284 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002285 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002286 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002287 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002288 pw.print(" Active at ");
2289 TimeUtils.formatDuration(job.madeActive - now, pw);
2290 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07002291 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2292 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07002293 }
2294 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002295 if (filterUid == -1) {
2296 pw.println();
2297 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2298 pw.print("mReportedActive="); pw.println(mReportedActive);
2299 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2300 }
Christopher Tate7060b042014-06-09 19:50:00 -07002301 }
2302 pw.println();
2303 }
2304}