blob: 8589de1a18849bcf80714a69d3e6896ef329818c [file] [log] [blame]
Christopher Tate7060b042014-06-09 19:50:00 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.job;
18
Christopher Tateb5c07882016-05-26 17:11:09 -070019import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
Shreyas Basarge5db09082016-01-07 13:38:29 +000022import java.io.FileDescriptor;
23import java.io.PrintWriter;
24import java.util.ArrayList;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070025import java.util.Arrays;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070026import java.util.Collections;
27import java.util.Comparator;
Shreyas Basarge5db09082016-01-07 13:38:29 +000028import java.util.Iterator;
29import java.util.List;
30
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070031import android.app.ActivityManager;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070032import android.app.ActivityManagerNative;
Christopher Tate5568f542014-06-18 13:53:31 -070033import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070034import android.app.IUidObserver;
Christopher Tate7060b042014-06-09 19:50:00 -070035import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000036import android.app.job.JobParameters;
Christopher Tate7060b042014-06-09 19:50:00 -070037import android.app.job.JobScheduler;
38import android.app.job.JobService;
Shreyas Basarge5db09082016-01-07 13:38:29 +000039import android.app.job.IJobScheduler;
Christopher Tate7060b042014-06-09 19:50:00 -070040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070042import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070043import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070046import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070047import android.content.pm.PackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070048import android.content.pm.ServiceInfo;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060049import android.content.pm.PackageManager.NameNotFoundException;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070050import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070051import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070052import android.os.BatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070053import android.os.Binder;
54import android.os.Handler;
55import android.os.Looper;
56import android.os.Message;
Shreyas Basargecbf5ae92016-03-08 16:13:06 +000057import android.os.Process;
Dianne Hackborn88e98df2015-03-23 13:29:14 -070058import android.os.PowerManager;
Christopher Tate7060b042014-06-09 19:50:00 -070059import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080060import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070061import android.os.ServiceManager;
Christopher Tate7060b042014-06-09 19:50:00 -070062import android.os.SystemClock;
63import android.os.UserHandle;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070064import android.provider.Settings;
65import android.util.KeyValueListParser;
Christopher Tate7060b042014-06-09 19:50:00 -070066import android.util.Slog;
67import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080068import android.util.SparseIntArray;
69import android.util.TimeUtils;
Christopher Tate5d346052016-03-08 12:56:08 -080070
Dianne Hackbornfdb19562014-07-11 16:03:36 -070071import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070072import com.android.internal.app.procstats.ProcessStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070073import com.android.internal.util.ArrayUtils;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080074import com.android.server.DeviceIdleController;
75import com.android.server.LocalServices;
Christopher Tate2f36fd62016-02-18 18:36:08 -080076import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080077import com.android.server.job.controllers.AppIdleController;
Christopher Tate7060b042014-06-09 19:50:00 -070078import com.android.server.job.controllers.BatteryController;
79import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080080import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070081import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070082import com.android.server.job.controllers.IdleController;
83import com.android.server.job.controllers.JobStatus;
84import com.android.server.job.controllers.StateController;
85import com.android.server.job.controllers.TimeController;
86
Jeff Sharkey822cbd12016-02-25 11:09:55 -070087import libcore.util.EmptyArray;
88
Christopher Tate7060b042014-06-09 19:50:00 -070089/**
90 * Responsible for taking jobs representing work to be performed by a client app, and determining
91 * based on the criteria specified when that job should be run against the client application's
92 * endpoint.
93 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
94 * about constraints, or the state of active jobs. It receives callbacks from the various
95 * controllers and completed jobs and operates accordingly.
96 *
97 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
98 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
99 * @hide
100 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800101public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700102 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800103 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700104 public static final boolean DEBUG = false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800105
Dianne Hackborn970510b2016-02-24 16:56:42 -0800106 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700107 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800108 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800109 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800110 /** The maximum number of jobs that we allow an unprivileged app to schedule */
111 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700112
Christopher Tate2f36fd62016-02-18 18:36:08 -0800113
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800114 /** Global local for all job scheduler state. */
115 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700116 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700117 final JobStore mJobs;
Dianne Hackborn807de782016-04-07 17:54:41 -0700118 /** Tracking amount of time each package runs for. */
119 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700120
121 static final int MSG_JOB_EXPIRED = 0;
122 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700123 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000124 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700125
Christopher Tate7060b042014-06-09 19:50:00 -0700126 /**
127 * Track Services that have currently active or pending jobs. The index is provided by
128 * {@link JobStatus#getServiceToken()}
129 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700130 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700131 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700132 List<StateController> mControllers;
Christopher Tate7060b042014-06-09 19:50:00 -0700133 /**
134 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
135 * when ready to execute them.
136 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700137 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700138
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700139 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700140
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700141 final JobHandler mHandler;
142 final JobSchedulerStub mJobSchedulerStub;
143
144 IBatteryStats mBatteryStats;
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700145 PowerManager mPowerManager;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800146 DeviceIdleController.LocalService mLocalDeviceIdleController;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700147
148 /**
149 * Set to true once we are allowed to run third party apps.
150 */
151 boolean mReadyToRock;
152
Christopher Tate7060b042014-06-09 19:50:00 -0700153 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800154 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800155 */
156 boolean mReportedActive;
157
158 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800159 * Current limit on the number of concurrent JobServiceContext entries we want to
160 * keep actively running a job.
161 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700162 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800163
164 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800165 * Which uids are currently in the foreground.
166 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800167 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
168
169 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
170
171 /**
172 * This array essentially stores the state of mActiveServices array.
173 * The ith index stores the job present on the ith JobServiceContext.
174 * We manipulate this array until we arrive at what jobs should be running on
175 * what JobServiceContext.
176 */
177 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
178 /**
179 * Indicates whether we need to act on this jobContext id
180 */
181 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
182 /**
183 * The uid whose jobs we would like to assign to a context.
184 */
185 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800186
187 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700188 * All times are in milliseconds. These constants are kept synchronized with the system
189 * global Settings. Any access to this class or its fields should be done while
190 * holding the JobSchedulerService.mLock lock.
191 */
192 private final class Constants extends ContentObserver {
193 // Key names stored in the settings value.
194 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
195 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
196 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
197 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
198 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
199 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
200 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
201 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
202 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
203 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
204 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
205 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
206
207 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
208 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
209 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
210 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
211 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
212 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
213 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
214 private static final int DEFAULT_FG_JOB_COUNT = 4;
215 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
216 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
217 private static final int DEFAULT_BG_LOW_JOB_COUNT = 2;
218 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
219
220 /**
221 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
222 * early.
223 */
224 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
225 /**
226 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
227 * things early.
228 */
229 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
230 /**
231 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
232 * things early. 1 == Run connectivity jobs as soon as ready.
233 */
234 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
235 /**
236 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
237 * schedule things early.
238 */
239 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
240 /**
241 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
242 * running some work early. This (and thus the other min counts) is now set to 1, to
243 * prevent any batching at this level. Since we now do batching through doze, that is
244 * a much better mechanism.
245 */
246 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
247 /**
248 * This is the job execution factor that is considered to be heavy use of the system.
249 */
250 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
251 /**
252 * This is the job execution factor that is considered to be moderate use of the system.
253 */
254 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
255 /**
256 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
257 */
258 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
259 /**
260 * The maximum number of background jobs we allow when the system is in a normal
261 * memory state.
262 */
263 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
264 /**
265 * The maximum number of background jobs we allow when the system is in a moderate
266 * memory state.
267 */
268 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
269 /**
270 * The maximum number of background jobs we allow when the system is in a low
271 * memory state.
272 */
273 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
274 /**
275 * The maximum number of background jobs we allow when the system is in a critical
276 * memory state.
277 */
278 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
279
280 private ContentResolver mResolver;
281 private final KeyValueListParser mParser = new KeyValueListParser(',');
282
283 public Constants(Handler handler) {
284 super(handler);
285 }
286
287 public void start(ContentResolver resolver) {
288 mResolver = resolver;
289 mResolver.registerContentObserver(Settings.Global.getUriFor(
290 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
291 updateConstants();
292 }
293
294 @Override
295 public void onChange(boolean selfChange, Uri uri) {
296 updateConstants();
297 }
298
299 private void updateConstants() {
300 synchronized (mLock) {
301 try {
302 mParser.setString(Settings.Global.getString(mResolver,
303 Settings.Global.ALARM_MANAGER_CONSTANTS));
304 } catch (IllegalArgumentException e) {
305 // Failed to parse the settings string, log this and move on
306 // with defaults.
307 Slog.e(TAG, "Bad device idle settings", e);
308 }
309
310 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
311 DEFAULT_MIN_IDLE_COUNT);
312 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
313 DEFAULT_MIN_CHARGING_COUNT);
314 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
315 DEFAULT_MIN_CONNECTIVITY_COUNT);
316 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
317 DEFAULT_MIN_CONTENT_COUNT);
318 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
319 DEFAULT_MIN_READY_JOBS_COUNT);
320 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
321 DEFAULT_HEAVY_USE_FACTOR);
322 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
323 DEFAULT_MODERATE_USE_FACTOR);
324 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
325 DEFAULT_FG_JOB_COUNT);
326 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
327 DEFAULT_BG_NORMAL_JOB_COUNT);
328 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
329 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
330 }
331 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
332 DEFAULT_BG_MODERATE_JOB_COUNT);
333 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
334 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
335 }
336 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
337 DEFAULT_BG_LOW_JOB_COUNT);
338 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
339 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
340 }
341 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
342 DEFAULT_BG_CRITICAL_JOB_COUNT);
343 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
344 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
345 }
346 }
347 }
348
349 void dump(PrintWriter pw) {
350 pw.println(" Settings:");
351
352 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
353 pw.print(MIN_IDLE_COUNT); pw.println();
354
355 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
356 pw.print(MIN_CHARGING_COUNT); pw.println();
357
358 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
359 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
360
361 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
362 pw.print(MIN_CONTENT_COUNT); pw.println();
363
364 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
365 pw.print(MIN_READY_JOBS_COUNT); pw.println();
366
367 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
368 pw.print(HEAVY_USE_FACTOR); pw.println();
369
370 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
371 pw.print(MODERATE_USE_FACTOR); pw.println();
372
373 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
374 pw.print(FG_JOB_COUNT); pw.println();
375
376 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
377 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
378
379 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
380 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
381
382 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
383 pw.print(BG_LOW_JOB_COUNT); pw.println();
384
385 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
386 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
387 }
388 }
389
390 final Constants mConstants;
391
392 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700393 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
394 * still clean up. On reinstall the package will have a new uid.
395 */
396 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
397 @Override
398 public void onReceive(Context context, Intent intent) {
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700399 if (DEBUG) {
400 Slog.d(TAG, "Receieved: " + intent.getAction());
401 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700402 if (Intent.ACTION_PACKAGE_CHANGED.equals(intent.getAction())) {
403 // Purge the app's jobs if the whole package was just disabled. When this is
404 // the case the component name will be a bare package name.
405 final String pkgName = getPackageName(intent);
406 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
407 if (pkgName != null && pkgUid != -1) {
408 final String[] changedComponents = intent.getStringArrayExtra(
409 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
410 if (changedComponents != null) {
411 for (String component : changedComponents) {
412 if (component.equals(pkgName)) {
413 if (DEBUG) {
414 Slog.d(TAG, "Package state change: " + pkgName);
415 }
416 try {
417 final int userId = UserHandle.getUserId(pkgUid);
418 IPackageManager pm = AppGlobals.getPackageManager();
419 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
420 if (state == COMPONENT_ENABLED_STATE_DISABLED
421 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
422 if (DEBUG) {
423 Slog.d(TAG, "Removing jobs for package " + pkgName
424 + " in user " + userId);
425 }
426 cancelJobsForUid(pkgUid, true);
427 }
428 } catch (RemoteException e) { /* cannot happen */ }
429 break;
430 }
431 }
432 }
433 } else {
434 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
435 }
436 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700437 // If this is an outright uninstall rather than the first half of an
438 // app update sequence, cancel the jobs associated with the app.
439 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
440 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
441 if (DEBUG) {
442 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
443 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700444 cancelJobsForUid(uidRemoved, true);
Christopher Tate7060b042014-06-09 19:50:00 -0700445 }
Shreyas Basarge5db09082016-01-07 13:38:29 +0000446 } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
Christopher Tate7060b042014-06-09 19:50:00 -0700447 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
448 if (DEBUG) {
449 Slog.d(TAG, "Removing jobs for user: " + userId);
450 }
451 cancelJobsForUser(userId);
452 }
453 }
454 };
455
Christopher Tateb5c07882016-05-26 17:11:09 -0700456 private String getPackageName(Intent intent) {
457 Uri uri = intent.getData();
458 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
459 return pkg;
460 }
461
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700462 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
463 @Override public void onUidStateChanged(int uid, int procState) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800464 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700465 }
466
467 @Override public void onUidGone(int uid) throws RemoteException {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800468 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700469 }
470
471 @Override public void onUidActive(int uid) throws RemoteException {
472 }
473
474 @Override public void onUidIdle(int uid) throws RemoteException {
475 cancelJobsForUid(uid, false);
476 }
477 };
478
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800479 public Object getLock() {
480 return mLock;
481 }
482
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700483 public JobStore getJobStore() {
484 return mJobs;
485 }
486
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700487 @Override
488 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700489 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
490 // Let's kick any outstanding jobs for this user.
491 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
492 }
493
494 @Override
495 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700496 // Let's kick any outstanding jobs for this user.
497 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
498 }
499
500 @Override
501 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700502 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700503 }
504
Christopher Tate7060b042014-06-09 19:50:00 -0700505 /**
506 * Entry point from client to schedule the provided job.
507 * This cancels the job if it's already been scheduled, and replaces it with the one provided.
508 * @param job JobInfo object containing execution parameters
509 * @param uId The package identifier of the application this job is for.
Christopher Tate7060b042014-06-09 19:50:00 -0700510 * @return Result of this operation. See <code>JobScheduler#RESULT_*</code> return codes.
511 */
Matthew Williams900c67f2014-07-09 12:46:53 -0700512 public int schedule(JobInfo job, int uId) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800513 return scheduleAsPackage(job, uId, null, -1, null);
Shreyas Basarge968ac752016-01-11 23:09:26 +0000514 }
515
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800516 public int scheduleAsPackage(JobInfo job, int uId, String packageName, int userId,
517 String tag) {
518 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700519 try {
520 if (ActivityManagerNative.getDefault().getAppStartMode(uId,
521 job.getService().getPackageName()) == ActivityManager.APP_START_MODE_DISABLED) {
522 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
523 + " -- package not allowed to start");
524 return JobScheduler.RESULT_FAILURE;
525 }
526 } catch (RemoteException e) {
527 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800528 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
529 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800530 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800531 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800532 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800533 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
534 Slog.w(TAG, "Too many jobs for uid " + uId);
535 throw new IllegalStateException("Apps may not schedule more than "
536 + MAX_JOBS_PER_APP + " distinct jobs");
537 }
538 }
539
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800540 toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700541 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700542 cancelJobImpl(toCancel, jobStatus);
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700543 }
544 startTrackingJob(jobStatus, toCancel);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800545 }
Matthew Williamsbafeeb92014-08-08 11:51:06 -0700546 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700547 return JobScheduler.RESULT_SUCCESS;
548 }
549
550 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800551 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800552 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
553 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
554 for (int i = jobs.size() - 1; i >= 0; i--) {
555 JobStatus job = jobs.get(i);
556 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700557 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800558 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700559 }
Christopher Tate7060b042014-06-09 19:50:00 -0700560 }
561
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600562 public JobInfo getPendingJob(int uid, int jobId) {
563 synchronized (mLock) {
564 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
565 for (int i = jobs.size() - 1; i >= 0; i--) {
566 JobStatus job = jobs.get(i);
567 if (job.getJobId() == jobId) {
568 return job.getJob();
569 }
570 }
571 return null;
572 }
573 }
574
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700575 void cancelJobsForUser(int userHandle) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700576 List<JobStatus> jobsForUser;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800577 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700578 jobsForUser = mJobs.getJobsByUser(userHandle);
579 }
580 for (int i=0; i<jobsForUser.size(); i++) {
581 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700582 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700583 }
584 }
585
586 /**
587 * Entry point from client to cancel all jobs originating from their uid.
588 * This will remove the job from the master list, and cancel the job if it was staged for
589 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700590 * @param uid Uid to check against for removal of a job.
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700591 * @param forceAll If true, all jobs for the uid will be canceled; if false, only those
592 * whose apps are stopped.
Christopher Tate7060b042014-06-09 19:50:00 -0700593 */
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700594 public void cancelJobsForUid(int uid, boolean forceAll) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700595 List<JobStatus> jobsForUid;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800596 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700597 jobsForUid = mJobs.getJobsByUid(uid);
598 }
599 for (int i=0; i<jobsForUid.size(); i++) {
600 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700601 if (!forceAll) {
602 String packageName = toRemove.getServiceComponent().getPackageName();
603 try {
604 if (ActivityManagerNative.getDefault().getAppStartMode(uid, packageName)
605 != ActivityManager.APP_START_MODE_DISABLED) {
606 continue;
607 }
608 } catch (RemoteException e) {
609 }
610 }
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700611 cancelJobImpl(toRemove, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700612 }
613 }
614
615 /**
616 * Entry point from client to cancel the job corresponding to the jobId provided.
617 * This will remove the job from the master list, and cancel the job if it was staged for
618 * execution or being executed.
619 * @param uid Uid of the calling client.
620 * @param jobId Id of the job, provided at schedule-time.
621 */
622 public void cancelJob(int uid, int jobId) {
623 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800624 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700625 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Matthew Williams48a30db2014-09-23 13:39:36 -0700626 }
627 if (toCancel != null) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700628 cancelJobImpl(toCancel, null);
Christopher Tate7060b042014-06-09 19:50:00 -0700629 }
630 }
631
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700632 private void cancelJobImpl(JobStatus cancelled, JobStatus incomingJob) {
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800633 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700634 stopTrackingJob(cancelled, incomingJob, true /* writeBack */);
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800635 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -0700636 // Remove from pending queue.
Dianne Hackborn807de782016-04-07 17:54:41 -0700637 if (mPendingJobs.remove(cancelled)) {
638 mJobPackageTracker.noteNonpending(cancelled);
639 }
Matthew Williams48a30db2014-09-23 13:39:36 -0700640 // Cancel if running.
Shreyas Basarge5db09082016-01-07 13:38:29 +0000641 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED);
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800642 reportActive();
Matthew Williams48a30db2014-09-23 13:39:36 -0700643 }
Christopher Tate7060b042014-06-09 19:50:00 -0700644 }
645
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800646 void updateUidState(int uid, int procState) {
647 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800648 if (procState == ActivityManager.PROCESS_STATE_TOP) {
649 // Only use this if we are exactly the top app. All others can live
650 // with just the foreground priority. This means that persistent processes
651 // can never be the top app priority... that is fine.
652 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
653 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
654 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800655 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -0800656 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800657 }
658 }
659 }
660
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700661 @Override
662 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800663 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700664 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -0600665 // When becoming idle, make sure no jobs are actively running,
666 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700667 for (int i=0; i<mActiveServices.size(); i++) {
668 JobServiceContext jsc = mActiveServices.get(i);
669 final JobStatus executing = jsc.getRunningJob();
Jeff Sharkey34618b52016-06-01 15:51:19 -0600670 if (executing != null
671 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700672 jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
673 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700674 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700675 } else {
676 // When coming out of idle, allow thing to start back up.
677 if (mReadyToRock) {
678 if (mLocalDeviceIdleController != null) {
679 if (!mReportedActive) {
680 mReportedActive = true;
681 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700682 }
683 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700684 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700685 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700686 }
687 }
688 }
689
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800690 void reportActive() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000691 // active is true if pending queue contains jobs OR some job is running.
692 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800693 if (mPendingJobs.size() <= 0) {
694 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -0700695 final JobServiceContext jsc = mActiveServices.get(i);
696 final JobStatus job = jsc.getRunningJob();
697 if (job != null
698 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
699 && !job.dozeWhitelisted) {
700 // We will report active if we have a job running and it is not an exception
701 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800702 active = true;
703 break;
704 }
705 }
706 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000707
708 if (mReportedActive != active) {
709 mReportedActive = active;
710 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800711 mLocalDeviceIdleController.setJobsActive(active);
712 }
713 }
714 }
715
Christopher Tate7060b042014-06-09 19:50:00 -0700716 /**
717 * Initializes the system service.
718 * <p>
719 * Subclasses must define a single argument constructor that accepts the context
720 * and passes it to super.
721 * </p>
722 *
723 * @param context The system server context.
724 */
725 public JobSchedulerService(Context context) {
726 super(context);
Dianne Hackborn970e3f42016-06-01 10:55:13 -0700727 mHandler = new JobHandler(context.getMainLooper());
728 mConstants = new Constants(mHandler);
729 mJobSchedulerStub = new JobSchedulerStub();
730 mJobs = JobStore.initAndGet(this);
731
Christopher Tate7060b042014-06-09 19:50:00 -0700732 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700733 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -0700734 mControllers.add(ConnectivityController.get(this));
735 mControllers.add(TimeController.get(this));
736 mControllers.add(IdleController.get(this));
737 mControllers.add(BatteryController.get(this));
Amith Yamasanib0ff3222015-03-04 09:56:14 -0800738 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800739 mControllers.add(ContentObserverController.get(this));
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700740 mControllers.add(DeviceIdleJobsController.get(this));
Christopher Tate7060b042014-06-09 19:50:00 -0700741 }
742
743 @Override
744 public void onStart() {
Shreyas Basargecbf5ae92016-03-08 16:13:06 +0000745 publishLocalService(JobSchedulerInternal.class, new LocalService());
Christopher Tate7060b042014-06-09 19:50:00 -0700746 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
747 }
748
749 @Override
750 public void onBootPhase(int phase) {
751 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700752 mConstants.start(getContext().getContentResolver());
Shreyas Basarge5db09082016-01-07 13:38:29 +0000753 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -0700754 final IntentFilter filter = new IntentFilter();
755 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
756 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tate7060b042014-06-09 19:50:00 -0700757 filter.addDataScheme("package");
758 getContext().registerReceiverAsUser(
759 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
760 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
761 getContext().registerReceiverAsUser(
762 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Shreyas Basarge5db09082016-01-07 13:38:29 +0000763 mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700764 try {
765 ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800766 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
767 | ActivityManager.UID_OBSERVER_IDLE);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700768 } catch (RemoteException e) {
769 // ignored; both services live in system_server
770 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700771 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800772 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700773 // Let's go!
774 mReadyToRock = true;
775 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
776 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800777 mLocalDeviceIdleController
778 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700779 // Create the "runners".
780 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
781 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -0700782 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700783 getContext().getMainLooper()));
784 }
785 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -0800786 mJobs.forEachJob(new JobStatusFunctor() {
787 @Override
788 public void process(JobStatus job) {
789 for (int controller = 0; controller < mControllers.size(); controller++) {
790 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -0800791 sc.maybeStartTrackingJobLocked(job, null);
792 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700793 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800794 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700795 // GO GO GO!
796 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
797 }
Christopher Tate7060b042014-06-09 19:50:00 -0700798 }
799 }
800
801 /**
802 * Called when we have a job status object that we need to insert in our
803 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
804 * about.
805 */
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800806 private void startTrackingJob(JobStatus jobStatus, JobStatus lastJob) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800807 synchronized (mLock) {
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800808 final boolean update = mJobs.add(jobStatus);
809 if (mReadyToRock) {
810 for (int i = 0; i < mControllers.size(); i++) {
811 StateController controller = mControllers.get(i);
812 if (update) {
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700813 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800814 }
815 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700816 }
Christopher Tate7060b042014-06-09 19:50:00 -0700817 }
Christopher Tate7060b042014-06-09 19:50:00 -0700818 }
819 }
820
821 /**
822 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
823 * object removed.
824 */
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700825 private boolean stopTrackingJob(JobStatus jobStatus, JobStatus incomingJob,
826 boolean writeBack) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800827 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700828 // Remove from store as well as controllers.
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800829 final boolean removed = mJobs.remove(jobStatus, writeBack);
830 if (removed && mReadyToRock) {
831 for (int i=0; i<mControllers.size(); i++) {
832 StateController controller = mControllers.get(i);
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700833 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800834 }
Christopher Tate7060b042014-06-09 19:50:00 -0700835 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -0800836 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -0700837 }
Christopher Tate7060b042014-06-09 19:50:00 -0700838 }
839
Shreyas Basarge5db09082016-01-07 13:38:29 +0000840 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700841 for (int i=0; i<mActiveServices.size(); i++) {
842 JobServiceContext jsc = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700843 final JobStatus executing = jsc.getRunningJob();
844 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Shreyas Basarge5db09082016-01-07 13:38:29 +0000845 jsc.cancelExecutingJob(reason);
Christopher Tate7060b042014-06-09 19:50:00 -0700846 return true;
847 }
848 }
849 return false;
850 }
851
852 /**
853 * @param job JobStatus we are querying against.
854 * @return Whether or not the job represented by the status object is currently being run or
855 * is pending.
856 */
857 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700858 for (int i=0; i<mActiveServices.size(); i++) {
859 JobServiceContext serviceContext = mActiveServices.get(i);
Christopher Tate7060b042014-06-09 19:50:00 -0700860 final JobStatus running = serviceContext.getRunningJob();
861 if (running != null && running.matches(job.getUid(), job.getJobId())) {
862 return true;
863 }
864 }
865 return false;
866 }
867
Dianne Hackborn807de782016-04-07 17:54:41 -0700868 void noteJobsPending(List<JobStatus> jobs) {
869 for (int i = jobs.size() - 1; i >= 0; i--) {
870 JobStatus job = jobs.get(i);
871 mJobPackageTracker.notePending(job);
872 }
873 }
874
875 void noteJobsNonpending(List<JobStatus> jobs) {
876 for (int i = jobs.size() - 1; i >= 0; i--) {
877 JobStatus job = jobs.get(i);
878 mJobPackageTracker.noteNonpending(job);
879 }
880 }
881
Christopher Tate7060b042014-06-09 19:50:00 -0700882 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700883 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
884 * specify an override deadline on a failed job (the failed job will run even though it's not
885 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
886 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
887 *
Christopher Tate7060b042014-06-09 19:50:00 -0700888 * @param failureToReschedule Provided job status that we will reschedule.
889 * @return A newly instantiated JobStatus with the same constraints as the last job except
890 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700891 *
892 * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH
Christopher Tate7060b042014-06-09 19:50:00 -0700893 */
894 private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) {
895 final long elapsedNowMillis = SystemClock.elapsedRealtime();
896 final JobInfo job = failureToReschedule.getJob();
897
898 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -0700899 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
900 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -0700901
902 switch (job.getBackoffPolicy()) {
Matthew Williamsd1c06752014-08-22 14:15:28 -0700903 case JobInfo.BACKOFF_POLICY_LINEAR:
904 delayMillis = initialBackoffMillis * backoffAttempts;
Christopher Tate7060b042014-06-09 19:50:00 -0700905 break;
906 default:
907 if (DEBUG) {
908 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
909 }
Matthew Williamsd1c06752014-08-22 14:15:28 -0700910 case JobInfo.BACKOFF_POLICY_EXPONENTIAL:
911 delayMillis =
912 (long) Math.scalb(initialBackoffMillis, backoffAttempts - 1);
Christopher Tate7060b042014-06-09 19:50:00 -0700913 break;
914 }
Matthew Williamsd1c06752014-08-22 14:15:28 -0700915 delayMillis =
916 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800917 JobStatus newJob = new JobStatus(failureToReschedule, elapsedNowMillis + delayMillis,
Matthew Williamsd1c06752014-08-22 14:15:28 -0700918 JobStatus.NO_LATEST_RUNTIME, backoffAttempts);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800919 for (int ic=0; ic<mControllers.size(); ic++) {
920 StateController controller = mControllers.get(ic);
921 controller.rescheduleForFailure(newJob, failureToReschedule);
922 }
923 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -0700924 }
925
926 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -0700927 * Called after a periodic has executed so we can reschedule it. We take the last execution
928 * time of the job to be the time of completion (i.e. the time at which this function is
929 * called).
Christopher Tate7060b042014-06-09 19:50:00 -0700930 * This could be inaccurate b/c the job can run for as long as
931 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
932 * to underscheduling at least, rather than if we had taken the last execution time to be the
933 * start of the execution.
934 * @return A new job representing the execution criteria for this instantiation of the
935 * recurring job.
936 */
937 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
938 final long elapsedNow = SystemClock.elapsedRealtime();
939 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -0700940 long runEarly = 0L;
941
942 // If this periodic was rescheduled it won't have a deadline.
943 if (periodicToReschedule.hasDeadlineConstraint()) {
944 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
945 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +0000946 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -0700947 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +0000948 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
949 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -0700950
951 if (DEBUG) {
952 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
953 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
954 }
955 return new JobStatus(periodicToReschedule, newEarliestRunTimeElapsed,
956 newLatestRuntimeElapsed, 0 /* backoffAttempt */);
957 }
958
959 // JobCompletedListener implementations.
960
961 /**
962 * A job just finished executing. We fetch the
963 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
964 * whether we want to reschedule we readd it to the controllers.
965 * @param jobStatus Completed job.
966 * @param needsReschedule Whether the implementing class should reschedule this job.
967 */
968 @Override
969 public void onJobCompleted(JobStatus jobStatus, boolean needsReschedule) {
970 if (DEBUG) {
971 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
972 }
Shreyas Basarge73f10252016-02-11 17:06:13 +0000973 // Do not write back immediately if this is a periodic job. The job may get lost if system
974 // shuts down before it is added back.
Dianne Hackborn141f11c2016-04-05 15:46:12 -0700975 if (!stopTrackingJob(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -0700976 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700977 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -0700978 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800979 // We still want to check for jobs to execute, because this job may have
980 // scheduled a new job under the same job id, and now we can run it.
981 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700982 return;
983 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800984 // Note: there is a small window of time in here where, when rescheduling a job,
985 // we will stop monitoring its content providers. This should be fixed by stopping
986 // the old job after scheduling the new one, but since we have no lock held here
987 // that may cause ordering problems if the app removes jobStatus while in here.
Christopher Tate7060b042014-06-09 19:50:00 -0700988 if (needsReschedule) {
989 JobStatus rescheduled = getRescheduleJobForFailure(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800990 startTrackingJob(rescheduled, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -0700991 } else if (jobStatus.getJob().isPeriodic()) {
992 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800993 startTrackingJob(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -0700994 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000995 reportActive();
996 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -0700997 }
998
999 // StateChangedListener implementations.
1000
1001 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001002 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1003 * some controller's state has changed, so as to run through the list of jobs and start/stop
1004 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001005 */
1006 @Override
1007 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001008 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001009 }
1010
1011 @Override
1012 public void onRunJobNow(JobStatus jobStatus) {
1013 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1014 }
1015
Christopher Tate7060b042014-06-09 19:50:00 -07001016 private class JobHandler extends Handler {
1017
1018 public JobHandler(Looper looper) {
1019 super(looper);
1020 }
1021
1022 @Override
1023 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001024 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001025 if (!mReadyToRock) {
1026 return;
1027 }
1028 }
Christopher Tate7060b042014-06-09 19:50:00 -07001029 switch (message.what) {
1030 case MSG_JOB_EXPIRED:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001031 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001032 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001033 // runNow can be null, which is a controller's way of indicating that its
1034 // state is such that all ready jobs should be run immediately.
Matthew Williams48a30db2014-09-23 13:39:36 -07001035 if (runNow != null && !mPendingJobs.contains(runNow)
1036 && mJobs.containsJob(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001037 mJobPackageTracker.notePending(runNow);
Christopher Tate7060b042014-06-09 19:50:00 -07001038 mPendingJobs.add(runNow);
1039 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001040 queueReadyJobsForExecutionLockedH();
Christopher Tate7060b042014-06-09 19:50:00 -07001041 }
Christopher Tate7060b042014-06-09 19:50:00 -07001042 break;
1043 case MSG_CHECK_JOB:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001044 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001045 if (mReportedActive) {
1046 // if jobs are currently being run, queue all ready jobs for execution.
1047 queueReadyJobsForExecutionLockedH();
1048 } else {
1049 // Check the list of jobs and run some of them if we feel inclined.
1050 maybeQueueReadyJobsForExecutionLockedH();
1051 }
1052 }
1053 break;
1054 case MSG_CHECK_JOB_GREEDY:
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001055 synchronized (mLock) {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001056 queueReadyJobsForExecutionLockedH();
Matthew Williams48a30db2014-09-23 13:39:36 -07001057 }
Christopher Tate7060b042014-06-09 19:50:00 -07001058 break;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001059 case MSG_STOP_JOB:
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001060 cancelJobImpl((JobStatus)message.obj, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001061 break;
Christopher Tate7060b042014-06-09 19:50:00 -07001062 }
1063 maybeRunPendingJobsH();
1064 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1065 removeMessages(MSG_CHECK_JOB);
1066 }
1067
1068 /**
1069 * Run through list of jobs and execute all possible - at least one is expired so we do
1070 * as many as we can.
1071 */
Matthew Williams48a30db2014-09-23 13:39:36 -07001072 private void queueReadyJobsForExecutionLockedH() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001073 if (DEBUG) {
1074 Slog.d(TAG, "queuing all ready jobs for execution:");
1075 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001076 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001077 mPendingJobs.clear();
1078 mJobs.forEachJob(mReadyQueueFunctor);
1079 mReadyQueueFunctor.postProcess();
1080
Matthew Williams48a30db2014-09-23 13:39:36 -07001081 if (DEBUG) {
1082 final int queuedJobs = mPendingJobs.size();
1083 if (queuedJobs == 0) {
1084 Slog.d(TAG, "No jobs pending.");
1085 } else {
1086 Slog.d(TAG, queuedJobs + " jobs queued.");
Matthew Williams75fc5252014-09-02 16:17:53 -07001087 }
Christopher Tate7060b042014-06-09 19:50:00 -07001088 }
1089 }
1090
Christopher Tate2f36fd62016-02-18 18:36:08 -08001091 class ReadyJobQueueFunctor implements JobStatusFunctor {
1092 ArrayList<JobStatus> newReadyJobs;
1093
1094 @Override
1095 public void process(JobStatus job) {
1096 if (isReadyToBeExecutedLocked(job)) {
1097 if (DEBUG) {
1098 Slog.d(TAG, " queued " + job.toShortString());
1099 }
1100 if (newReadyJobs == null) {
1101 newReadyJobs = new ArrayList<JobStatus>();
1102 }
1103 newReadyJobs.add(job);
1104 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
1105 stopJobOnServiceContextLocked(job,
1106 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
1107 }
1108 }
1109
1110 public void postProcess() {
1111 if (newReadyJobs != null) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001112 noteJobsPending(newReadyJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001113 mPendingJobs.addAll(newReadyJobs);
1114 }
1115 newReadyJobs = null;
1116 }
1117 }
1118 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1119
Christopher Tate7060b042014-06-09 19:50:00 -07001120 /**
1121 * The state of at least one job has changed. Here is where we could enforce various
1122 * policies on when we want to execute jobs.
1123 * Right now the policy is such:
1124 * If >1 of the ready jobs is idle mode we send all of them off
1125 * if more than 2 network connectivity jobs are ready we send them all off.
1126 * If more than 4 jobs total are ready we send them all off.
1127 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1128 */
Christopher Tate2f36fd62016-02-18 18:36:08 -08001129 class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1130 int chargingCount;
1131 int idleCount;
1132 int backoffCount;
1133 int connectivityCount;
1134 int contentCount;
1135 List<JobStatus> runnableJobs;
1136
1137 public MaybeReadyJobQueueFunctor() {
1138 reset();
1139 }
1140
1141 // Functor method invoked for each job via JobStore.forEachJob()
1142 @Override
1143 public void process(JobStatus job) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001144 if (isReadyToBeExecutedLocked(job)) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001145 try {
1146 if (ActivityManagerNative.getDefault().getAppStartMode(job.getUid(),
1147 job.getJob().getService().getPackageName())
1148 == ActivityManager.APP_START_MODE_DISABLED) {
1149 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1150 + job.getJob().toString() + " -- package not allowed to start");
1151 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001152 return;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001153 }
1154 } catch (RemoteException e) {
1155 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001156 if (job.getNumFailures() > 0) {
1157 backoffCount++;
Christopher Tate7060b042014-06-09 19:50:00 -07001158 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001159 if (job.hasIdleConstraint()) {
1160 idleCount++;
1161 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001162 if (job.hasConnectivityConstraint() || job.hasUnmeteredConstraint()
1163 || job.hasNotRoamingConstraint()) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001164 connectivityCount++;
1165 }
1166 if (job.hasChargingConstraint()) {
1167 chargingCount++;
1168 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001169 if (job.hasContentTriggerConstraint()) {
1170 contentCount++;
1171 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001172 if (runnableJobs == null) {
1173 runnableJobs = new ArrayList<>();
1174 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001175 runnableJobs.add(job);
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001176 } else if (areJobConstraintsNotSatisfiedLocked(job)) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001177 stopJobOnServiceContextLocked(job,
1178 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED);
Christopher Tate7060b042014-06-09 19:50:00 -07001179 }
Matthew Williams48a30db2014-09-23 13:39:36 -07001180 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001181
1182 public void postProcess() {
1183 if (backoffCount > 0 ||
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001184 idleCount >= mConstants.MIN_IDLE_COUNT ||
1185 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1186 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1187 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1188 (runnableJobs != null
1189 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001190 if (DEBUG) {
1191 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Running jobs.");
1192 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001193 noteJobsPending(runnableJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001194 mPendingJobs.addAll(runnableJobs);
1195 } else {
1196 if (DEBUG) {
1197 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLockedH: Not running anything.");
1198 }
Christopher Tate7060b042014-06-09 19:50:00 -07001199 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001200
1201 // Be ready for next time
1202 reset();
Matthew Williams48a30db2014-09-23 13:39:36 -07001203 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001204
1205 private void reset() {
1206 chargingCount = 0;
1207 idleCount = 0;
1208 backoffCount = 0;
1209 connectivityCount = 0;
1210 contentCount = 0;
1211 runnableJobs = null;
1212 }
1213 }
1214 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1215
1216 private void maybeQueueReadyJobsForExecutionLockedH() {
1217 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1218
Dianne Hackborn807de782016-04-07 17:54:41 -07001219 noteJobsNonpending(mPendingJobs);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001220 mPendingJobs.clear();
1221 mJobs.forEachJob(mMaybeQueueFunctor);
1222 mMaybeQueueFunctor.postProcess();
Christopher Tate7060b042014-06-09 19:50:00 -07001223 }
1224
1225 /**
1226 * Criteria for moving a job into the pending queue:
1227 * - It's ready.
1228 * - It's not pending.
1229 * - It's not already running on a JSC.
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001230 * - The user that requested the job is running.
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001231 * - The component is enabled and runnable.
Christopher Tate7060b042014-06-09 19:50:00 -07001232 */
1233 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001234 final boolean jobReady = job.isReady();
1235 final boolean jobPending = mPendingJobs.contains(job);
1236 final boolean jobActive = isCurrentlyActiveLocked(job);
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001237
1238 final int userId = job.getUserId();
1239 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1240 final boolean componentPresent;
1241 try {
1242 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1243 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1244 userId) != null);
1245 } catch (RemoteException e) {
1246 throw e.rethrowAsRuntimeException();
1247 }
1248
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001249 if (DEBUG) {
1250 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1251 + " ready=" + jobReady + " pending=" + jobPending
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001252 + " active=" + jobActive + " userStarted=" + userStarted
1253 + " componentPresent=" + componentPresent);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001254 }
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001255 return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
Christopher Tate7060b042014-06-09 19:50:00 -07001256 }
1257
1258 /**
1259 * Criteria for cancelling an active job:
1260 * - It's not ready
1261 * - It's running on a JSC.
1262 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001263 private boolean areJobConstraintsNotSatisfiedLocked(JobStatus job) {
Christopher Tate7060b042014-06-09 19:50:00 -07001264 return !job.isReady() && isCurrentlyActiveLocked(job);
1265 }
1266
1267 /**
1268 * Reconcile jobs in the pending queue against available execution contexts.
1269 * A controller can force a job into the pending queue even if it's already running, but
1270 * here is where we decide whether to actually execute it.
1271 */
1272 private void maybeRunPendingJobsH() {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001273 synchronized (mLock) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001274 if (DEBUG) {
1275 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1276 }
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001277 assignJobsToContextsLocked();
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001278 reportActive();
Christopher Tate7060b042014-06-09 19:50:00 -07001279 }
1280 }
1281 }
1282
Dianne Hackborn807de782016-04-07 17:54:41 -07001283 private int adjustJobPriority(int curPriority, JobStatus job) {
1284 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1285 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001286 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001287 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001288 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001289 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1290 }
1291 }
1292 return curPriority;
1293 }
1294
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001295 private int evaluateJobPriorityLocked(JobStatus job) {
1296 int priority = job.getPriority();
1297 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001298 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001299 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001300 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1301 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001302 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001303 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001304 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001305 }
1306
Christopher Tate7060b042014-06-09 19:50:00 -07001307 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001308 * Takes jobs from pending queue and runs them on available contexts.
1309 * If no contexts are available, preempts lower priority jobs to
1310 * run higher priority ones.
1311 * Lock on mJobs before calling this function.
1312 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001313 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001314 if (DEBUG) {
1315 Slog.d(TAG, printPendingQueue());
1316 }
1317
Dianne Hackborn970510b2016-02-24 16:56:42 -08001318 int memLevel;
1319 try {
1320 memLevel = ActivityManagerNative.getDefault().getMemoryTrimLevel();
1321 } catch (RemoteException e) {
1322 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1323 }
1324 switch (memLevel) {
1325 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001326 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001327 break;
1328 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001329 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001330 break;
1331 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001332 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001333 break;
1334 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001335 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001336 break;
1337 }
1338
1339 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1340 boolean[] act = mTmpAssignAct;
1341 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1342 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001343 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001344 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1345 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001346 final JobStatus status = js.getRunningJob();
1347 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001348 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001349 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1350 numForeground++;
1351 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001352 }
1353 act[i] = false;
1354 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001355 }
1356 if (DEBUG) {
1357 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1358 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001359 for (int i=0; i<mPendingJobs.size(); i++) {
1360 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001361
1362 // If job is already running, go to next job.
1363 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1364 if (jobRunningContext != -1) {
1365 continue;
1366 }
1367
Dianne Hackborn970510b2016-02-24 16:56:42 -08001368 final int priority = evaluateJobPriorityLocked(nextPending);
1369 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001370
Shreyas Basarge5db09082016-01-07 13:38:29 +00001371 // Find a context for nextPending. The context should be available OR
1372 // it should have lowest priority among all running jobs
1373 // (sharing the same Uid as nextPending)
1374 int minPriority = Integer.MAX_VALUE;
1375 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001376 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1377 JobStatus job = contextIdToJobMap[j];
1378 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001379 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001380 if ((numActive < mMaxActiveJobs ||
1381 (priority >= JobInfo.PRIORITY_TOP_APP &&
1382 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001383 (preferredUid == nextPending.getUid() ||
1384 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1385 // This slot is free, and we haven't yet hit the limit on
1386 // concurrent jobs... we can just throw the job in to here.
1387 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001388 break;
1389 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001390 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001391 // the context has a preferred Uid or we have reached the limit on
1392 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001393 continue;
1394 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001395 if (job.getUid() != nextPending.getUid()) {
1396 continue;
1397 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001398 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001399 continue;
1400 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001401 if (minPriority > nextPending.lastEvaluatedPriority) {
1402 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001403 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001404 }
1405 }
1406 if (minPriorityContextId != -1) {
1407 contextIdToJobMap[minPriorityContextId] = nextPending;
1408 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001409 numActive++;
1410 if (priority >= JobInfo.PRIORITY_TOP_APP) {
1411 numForeground++;
1412 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001413 }
1414 }
1415 if (DEBUG) {
1416 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
1417 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001418 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001419 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001420 boolean preservePreferredUid = false;
1421 if (act[i]) {
1422 JobStatus js = mActiveServices.get(i).getRunningJob();
1423 if (js != null) {
1424 if (DEBUG) {
1425 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
1426 }
1427 // preferredUid will be set to uid of currently running job.
1428 mActiveServices.get(i).preemptExecutingJob();
1429 preservePreferredUid = true;
1430 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001431 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00001432 if (DEBUG) {
1433 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001434 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001435 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001436 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001437 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001438 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001439 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
1440 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001441 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001442 if (mPendingJobs.remove(pendingJob)) {
1443 mJobPackageTracker.noteNonpending(pendingJob);
1444 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001445 }
1446 }
1447 if (!preservePreferredUid) {
1448 mActiveServices.get(i).clearPreferredUid();
1449 }
1450 }
1451 }
1452
1453 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
1454 for (int i=0; i<map.length; i++) {
1455 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
1456 return i;
1457 }
1458 }
1459 return -1;
1460 }
1461
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001462 final class LocalService implements JobSchedulerInternal {
1463
1464 /**
1465 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
1466 * jobs are always considered pending.
1467 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001468 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00001469 public List<JobInfo> getSystemScheduledPendingJobs() {
1470 synchronized (mLock) {
1471 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
1472 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
1473 @Override
1474 public void process(JobStatus job) {
1475 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
1476 pendingJobs.add(job.getJob());
1477 }
1478 }
1479 });
1480 return pendingJobs;
1481 }
1482 }
1483 }
1484
Shreyas Basarge5db09082016-01-07 13:38:29 +00001485 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001486 * Binder stub trampoline implementation
1487 */
1488 final class JobSchedulerStub extends IJobScheduler.Stub {
1489 /** Cache determination of whether a given app can persist jobs
1490 * key is uid of the calling app; value is undetermined/true/false
1491 */
1492 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
1493
1494 // Enforce that only the app itself (or shared uid participant) can schedule a
1495 // job that runs one of the app's services, as well as verifying that the
1496 // named service properly requires the BIND_JOB_SERVICE permission
1497 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07001498 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07001499 final ComponentName service = job.getService();
1500 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07001501 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06001502 PackageManager.MATCH_DIRECT_BOOT_AWARE
1503 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07001504 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07001505 if (si == null) {
1506 throw new IllegalArgumentException("No such service " + service);
1507 }
Christopher Tate7060b042014-06-09 19:50:00 -07001508 if (si.applicationInfo.uid != uid) {
1509 throw new IllegalArgumentException("uid " + uid +
1510 " cannot schedule job in " + service.getPackageName());
1511 }
1512 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
1513 throw new IllegalArgumentException("Scheduled service " + service
1514 + " does not require android.permission.BIND_JOB_SERVICE permission");
1515 }
Christopher Tate5568f542014-06-18 13:53:31 -07001516 } catch (RemoteException e) {
1517 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07001518 }
1519 }
1520
1521 private boolean canPersistJobs(int pid, int uid) {
1522 // If we get this far we're good to go; all we need to do now is check
1523 // whether the app is allowed to persist its scheduled work.
1524 final boolean canPersist;
1525 synchronized (mPersistCache) {
1526 Boolean cached = mPersistCache.get(uid);
1527 if (cached != null) {
1528 canPersist = cached.booleanValue();
1529 } else {
1530 // Persisting jobs is tantamount to running at boot, so we permit
1531 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
1532 // permission
1533 int result = getContext().checkPermission(
1534 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
1535 canPersist = (result == PackageManager.PERMISSION_GRANTED);
1536 mPersistCache.put(uid, canPersist);
1537 }
1538 }
1539 return canPersist;
1540 }
1541
1542 // IJobScheduler implementation
1543 @Override
1544 public int schedule(JobInfo job) throws RemoteException {
1545 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001546 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07001547 }
1548 final int pid = Binder.getCallingPid();
1549 final int uid = Binder.getCallingUid();
1550
1551 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07001552 if (job.isPersisted()) {
1553 if (!canPersistJobs(pid, uid)) {
1554 throw new IllegalArgumentException("Error: requested job be persisted without"
1555 + " holding RECEIVE_BOOT_COMPLETED permission.");
1556 }
1557 }
Christopher Tate7060b042014-06-09 19:50:00 -07001558
1559 long ident = Binder.clearCallingIdentity();
1560 try {
Matthew Williams900c67f2014-07-09 12:46:53 -07001561 return JobSchedulerService.this.schedule(job, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07001562 } finally {
1563 Binder.restoreCallingIdentity(ident);
1564 }
1565 }
1566
1567 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001568 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00001569 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001570 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00001571 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001572 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
1573 + " on behalf of " + packageName);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001574 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001575
1576 if (packageName == null) {
1577 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00001578 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001579
1580 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
1581 android.Manifest.permission.UPDATE_DEVICE_STATS);
1582 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
1583 throw new SecurityException("Caller uid " + callerUid
1584 + " not permitted to schedule jobs for other apps");
1585 }
1586
Jeff Sharkey4f100402016-05-03 17:44:23 -06001587 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
1588 getContext().enforceCallingOrSelfPermission(
1589 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
1590 }
1591
Shreyas Basarge968ac752016-01-11 23:09:26 +00001592 long ident = Binder.clearCallingIdentity();
1593 try {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001594 return JobSchedulerService.this.scheduleAsPackage(job, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001595 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00001596 } finally {
1597 Binder.restoreCallingIdentity(ident);
1598 }
1599 }
1600
1601 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001602 public List<JobInfo> getAllPendingJobs() throws RemoteException {
1603 final int uid = Binder.getCallingUid();
1604
1605 long ident = Binder.clearCallingIdentity();
1606 try {
1607 return JobSchedulerService.this.getPendingJobs(uid);
1608 } finally {
1609 Binder.restoreCallingIdentity(ident);
1610 }
1611 }
1612
1613 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001614 public JobInfo getPendingJob(int jobId) throws RemoteException {
1615 final int uid = Binder.getCallingUid();
1616
1617 long ident = Binder.clearCallingIdentity();
1618 try {
1619 return JobSchedulerService.this.getPendingJob(uid, jobId);
1620 } finally {
1621 Binder.restoreCallingIdentity(ident);
1622 }
1623 }
1624
1625 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07001626 public void cancelAll() throws RemoteException {
1627 final int uid = Binder.getCallingUid();
1628
1629 long ident = Binder.clearCallingIdentity();
1630 try {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001631 JobSchedulerService.this.cancelJobsForUid(uid, true);
Christopher Tate7060b042014-06-09 19:50:00 -07001632 } finally {
1633 Binder.restoreCallingIdentity(ident);
1634 }
1635 }
1636
1637 @Override
1638 public void cancel(int jobId) throws RemoteException {
1639 final int uid = Binder.getCallingUid();
1640
1641 long ident = Binder.clearCallingIdentity();
1642 try {
1643 JobSchedulerService.this.cancelJob(uid, jobId);
1644 } finally {
1645 Binder.restoreCallingIdentity(ident);
1646 }
1647 }
1648
1649 /**
1650 * "dumpsys" infrastructure
1651 */
1652 @Override
1653 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1654 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
1655
1656 long identityToken = Binder.clearCallingIdentity();
1657 try {
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001658 JobSchedulerService.this.dumpInternal(pw, args);
Christopher Tate7060b042014-06-09 19:50:00 -07001659 } finally {
1660 Binder.restoreCallingIdentity(identityToken);
1661 }
1662 }
Christopher Tate5d346052016-03-08 12:56:08 -08001663
1664 @Override
1665 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1666 String[] args, ResultReceiver resultReceiver) throws RemoteException {
1667 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
1668 this, in, out, err, args, resultReceiver);
1669 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001670 };
1671
Christopher Tate5d346052016-03-08 12:56:08 -08001672 // Shell command infrastructure: run the given job immediately
1673 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
1674 if (DEBUG) {
1675 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
1676 + " " + jobId + " f=" + force);
1677 }
1678
1679 try {
1680 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0, userId);
1681 if (uid < 0) {
1682 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
1683 }
1684
1685 synchronized (mLock) {
1686 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
1687 if (js == null) {
1688 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
1689 }
1690
1691 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
1692 if (!js.isConstraintsSatisfied()) {
1693 js.overrideState = 0;
1694 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
1695 }
1696
1697 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
1698 }
1699 } catch (RemoteException e) {
1700 // can't happen
1701 }
1702 return 0;
1703 }
1704
Shreyas Basarge5db09082016-01-07 13:38:29 +00001705 private String printContextIdToJobMap(JobStatus[] map, String initial) {
1706 StringBuilder s = new StringBuilder(initial + ": ");
1707 for (int i=0; i<map.length; i++) {
1708 s.append("(")
1709 .append(map[i] == null? -1: map[i].getJobId())
1710 .append(map[i] == null? -1: map[i].getUid())
1711 .append(")" );
1712 }
1713 return s.toString();
1714 }
1715
1716 private String printPendingQueue() {
1717 StringBuilder s = new StringBuilder("Pending queue: ");
1718 Iterator<JobStatus> it = mPendingJobs.iterator();
1719 while (it.hasNext()) {
1720 JobStatus js = it.next();
1721 s.append("(")
1722 .append(js.getJob().getId())
1723 .append(", ")
1724 .append(js.getUid())
1725 .append(") ");
1726 }
1727 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07001728 }
Christopher Tate7060b042014-06-09 19:50:00 -07001729
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001730 static void dumpHelp(PrintWriter pw) {
1731 pw.println("Job Scheduler (jobscheduler) dump options:");
1732 pw.println(" [-h] [package] ...");
1733 pw.println(" -h: print this help");
1734 pw.println(" [package] is an optional package name to limit the output to.");
1735 }
1736
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001737 void dumpInternal(final PrintWriter pw, String[] args) {
1738 int filterUid = -1;
1739 if (!ArrayUtils.isEmpty(args)) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001740 int opti = 0;
1741 while (opti < args.length) {
1742 String arg = args[opti];
1743 if ("-h".equals(arg)) {
1744 dumpHelp(pw);
1745 return;
1746 } else if ("-a".equals(arg)) {
1747 // Ignore, we always dump all.
1748 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
1749 pw.println("Unknown option: " + arg);
1750 return;
1751 } else {
1752 break;
1753 }
1754 opti++;
1755 }
1756 if (opti < args.length) {
1757 String pkg = args[opti];
1758 try {
1759 filterUid = getContext().getPackageManager().getPackageUid(pkg,
1760 PackageManager.MATCH_UNINSTALLED_PACKAGES);
1761 } catch (NameNotFoundException ignored) {
1762 pw.println("Invalid package: " + pkg);
1763 return;
1764 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001765 }
1766 }
1767
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001768 final int filterUidFinal = UserHandle.getAppId(filterUid);
Christopher Tatef973a7b2014-08-29 12:54:08 -07001769 final long now = SystemClock.elapsedRealtime();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001770 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001771 mConstants.dump(pw);
1772 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001773 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001774 pw.print("Registered ");
1775 pw.print(mJobs.size());
1776 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07001777 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001778 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
1779 Collections.sort(jobs, new Comparator<JobStatus>() {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001780 @Override
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001781 public int compare(JobStatus o1, JobStatus o2) {
1782 int uid1 = o1.getUid();
1783 int uid2 = o2.getUid();
1784 int id1 = o1.getJobId();
1785 int id2 = o2.getJobId();
1786 if (uid1 != uid2) {
1787 return uid1 < uid2 ? -1 : 1;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001788 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001789 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001790 }
1791 });
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001792 for (JobStatus job : jobs) {
1793 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
1794 pw.println(job.toShortStringExceptUniqueId());
1795
1796 // Skip printing details if the caller requested a filter
1797 if (!job.shouldDump(filterUidFinal)) {
1798 continue;
1799 }
1800
1801 job.dump(pw, " ", true);
1802 pw.print(" Ready: ");
1803 pw.print(mHandler.isReadyToBeExecutedLocked(job));
1804 pw.print(" (job=");
1805 pw.print(job.isReady());
1806 pw.print(" pending=");
1807 pw.print(mPendingJobs.contains(job));
1808 pw.print(" active=");
1809 pw.print(isCurrentlyActiveLocked(job));
1810 pw.print(" user=");
1811 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
1812 pw.println(")");
1813 }
Christopher Tate7060b042014-06-09 19:50:00 -07001814 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07001815 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07001816 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001817 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07001818 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001819 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07001820 }
1821 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001822 pw.println("Uid priority overrides:");
1823 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001824 int uid = mUidPriorityOverride.keyAt(i);
1825 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
1826 pw.print(" "); pw.print(UserHandle.formatUid(uid));
1827 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
1828 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001829 }
1830 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001831 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07001832 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001833 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
1834 pw.println();
1835 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001836 pw.println("Pending queue:");
1837 for (int i=0; i<mPendingJobs.size(); i++) {
1838 JobStatus job = mPendingJobs.get(i);
1839 pw.print(" Pending #"); pw.print(i); pw.print(": ");
1840 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08001841 job.dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001842 int priority = evaluateJobPriorityLocked(job);
1843 if (priority != JobInfo.PRIORITY_DEFAULT) {
1844 pw.print(" Evaluated priority: "); pw.println(priority);
1845 }
1846 pw.print(" Tag: "); pw.println(job.getTag());
1847 }
Christopher Tate7060b042014-06-09 19:50:00 -07001848 pw.println();
1849 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001850 for (int i=0; i<mActiveServices.size(); i++) {
1851 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08001852 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Shreyas Basarge5db09082016-01-07 13:38:29 +00001853 if (jsc.getRunningJob() == null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001854 pw.println("inactive");
Christopher Tate7060b042014-06-09 19:50:00 -07001855 continue;
1856 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001857 pw.println(jsc.getRunningJob().toShortString());
1858 pw.print(" Running for: ");
1859 TimeUtils.formatDuration(now - jsc.getExecutionStartTimeElapsed(), pw);
1860 pw.print(", timeout at: ");
1861 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - now, pw);
1862 pw.println();
1863 jsc.getRunningJob().dump(pw, " ", false);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001864 int priority = evaluateJobPriorityLocked(jsc.getRunningJob());
1865 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001866 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001867 }
Christopher Tate7060b042014-06-09 19:50:00 -07001868 }
1869 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07001870 if (filterUid == -1) {
1871 pw.println();
1872 pw.print("mReadyToRock="); pw.println(mReadyToRock);
1873 pw.print("mReportedActive="); pw.println(mReportedActive);
1874 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
1875 }
Christopher Tate7060b042014-06-09 19:50:00 -07001876 }
1877 pw.println();
1878 }
1879}