blob: c087bff4e8a58209f299b15f440aa0f189a35901 [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
Christopher Tate435c2f42018-01-18 12:59:15 -080022import android.annotation.UserIdInt;
Christopher Tateee7805b2016-07-15 16:56:56 -070023import android.app.Activity;
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070024import android.app.ActivityManager;
Makoto Onuki15407842018-01-19 14:23:11 -080025import android.app.ActivityManagerInternal;
Christopher Tate5568f542014-06-18 13:53:31 -070026import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070027import android.app.IUidObserver;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070028import android.app.job.IJobScheduler;
Christopher Tate7060b042014-06-09 19:50:00 -070029import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000030import android.app.job.JobParameters;
Christopher Tate7060b042014-06-09 19:50:00 -070031import android.app.job.JobScheduler;
32import android.app.job.JobService;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070033import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080034import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070035import android.app.usage.UsageStatsManagerInternal;
36import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070037import android.content.BroadcastReceiver;
38import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070039import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070040import android.content.Context;
41import android.content.Intent;
42import android.content.IntentFilter;
Christopher Tate435c2f42018-01-18 12:59:15 -080043import android.content.Intent.UriFlags;
Christopher Tate5568f542014-06-18 13:53:31 -070044import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070045import android.content.pm.PackageManager;
Christopher Tatea732f012017-10-26 17:26:53 -070046import android.content.pm.PackageManagerInternal;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060047import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070048import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070049import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070050import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070051import android.os.BatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070052import android.os.Binder;
53import android.os.Handler;
54import android.os.Looper;
55import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070056import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070057import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080058import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070059import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070060import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070061import android.os.SystemClock;
62import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070063import android.os.UserManagerInternal;
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;
Tej Singhd5747a62018-01-08 20:57:35 -080069import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080070import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080071import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080072
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070073import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070074import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070075import com.android.internal.app.procstats.ProcessStats;
Christopher Tatea732f012017-10-26 17:26:53 -070076import com.android.internal.os.BackgroundThread;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070077import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060078import com.android.internal.util.DumpUtils;
Makoto Onuki15407842018-01-19 14:23:11 -080079import com.android.internal.util.Preconditions;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080080import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070081import com.android.server.FgThread;
Makoto Onuki15407842018-01-19 14:23:11 -080082import com.android.server.ForceAppStandbyTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080083import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080084import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
85import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Christopher Tate2f36fd62016-02-18 18:36:08 -080086import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080087import com.android.server.job.controllers.AppIdleController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070088import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070089import com.android.server.job.controllers.BatteryController;
90import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080091import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070092import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070093import com.android.server.job.controllers.IdleController;
94import com.android.server.job.controllers.JobStatus;
95import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070096import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -070097import com.android.server.job.controllers.TimeController;
98
Jeff Sharkey822cbd12016-02-25 11:09:55 -070099import libcore.util.EmptyArray;
100
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700101import java.io.FileDescriptor;
102import java.io.PrintWriter;
103import java.time.Clock;
104import java.util.ArrayList;
105import java.util.Arrays;
106import java.util.Collections;
107import java.util.Comparator;
108import java.util.Iterator;
109import java.util.List;
Makoto Onuki15407842018-01-19 14:23:11 -0800110import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700111
Christopher Tate7060b042014-06-09 19:50:00 -0700112/**
113 * Responsible for taking jobs representing work to be performed by a client app, and determining
114 * based on the criteria specified when that job should be run against the client application's
115 * endpoint.
116 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
117 * about constraints, or the state of active jobs. It receives callbacks from the various
118 * controllers and completed jobs and operates accordingly.
119 *
120 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
121 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
122 * @hide
123 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800124public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700125 implements StateChangedListener, JobCompletedListener {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800126 static final String TAG = "JobSchedulerService";
Matthew Williamsaa984312015-10-15 16:08:05 -0700127 public static final boolean DEBUG = false;
Christopher Tatea732f012017-10-26 17:26:53 -0700128 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800129
Dianne Hackborn970510b2016-02-24 16:56:42 -0800130 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700131 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800132 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800133 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800134 /** The maximum number of jobs that we allow an unprivileged app to schedule */
135 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700136
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700137 @VisibleForTesting
138 public static Clock sSystemClock = Clock.systemUTC();
139 @VisibleForTesting
140 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
141 @VisibleForTesting
142 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800143
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800144 /** Global local for all job scheduler state. */
145 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700146 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700147 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700148 /** Tracking the standby bucket state of each app */
149 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700150 /** Tracking amount of time each package runs for. */
151 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700152
153 static final int MSG_JOB_EXPIRED = 0;
154 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700155 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000156 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700157
Christopher Tate7060b042014-06-09 19:50:00 -0700158 /**
159 * Track Services that have currently active or pending jobs. The index is provided by
160 * {@link JobStatus#getServiceToken()}
161 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700162 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700163 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700164 List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800165 /** Need direct access to this for testing. */
166 BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700167 /** Need direct access to this for testing. */
168 StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700169 /** Need directly for sending uid state changes */
170 private BackgroundJobsController mBackgroundJobsController;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700171 private DeviceIdleJobsController mDeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -0700172 /**
173 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
174 * when ready to execute them.
175 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700176 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700177
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700178 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700179
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700180 final JobHandler mHandler;
181 final JobSchedulerStub mJobSchedulerStub;
182
Christopher Tatea732f012017-10-26 17:26:53 -0700183 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800184 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700185 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800186 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onuki15407842018-01-19 14:23:11 -0800187 final ForceAppStandbyTracker mForceAppStandbyTracker;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700188
189 /**
190 * Set to true once we are allowed to run third party apps.
191 */
192 boolean mReadyToRock;
193
Christopher Tate7060b042014-06-09 19:50:00 -0700194 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800195 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800196 */
197 boolean mReportedActive;
198
199 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800200 * Are we currently in device-wide standby parole?
201 */
202 volatile boolean mInParole;
203
204 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800205 * Current limit on the number of concurrent JobServiceContext entries we want to
206 * keep actively running a job.
207 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700208 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800209
210 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800211 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800212 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800213 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
214
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700215 /**
216 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
217 */
218 final SparseIntArray mBackingUpUids = new SparseIntArray();
219
Christopher Tatea732f012017-10-26 17:26:53 -0700220 /**
221 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
222 * next become runnable. Index into this array is by normalized bucket:
223 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
224 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
225 * and NEVER apps don't get them at all.
226 */
227 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
228 long mHeartbeat = 0;
229 long mLastHeartbeatTime = 0;
230
Dianne Hackborn970510b2016-02-24 16:56:42 -0800231 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
232
233 /**
234 * This array essentially stores the state of mActiveServices array.
235 * The ith index stores the job present on the ith JobServiceContext.
236 * We manipulate this array until we arrive at what jobs should be running on
237 * what JobServiceContext.
238 */
239 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
240 /**
241 * Indicates whether we need to act on this jobContext id
242 */
243 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
244 /**
245 * The uid whose jobs we would like to assign to a context.
246 */
247 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800248
249 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700250 * All times are in milliseconds. These constants are kept synchronized with the system
251 * global Settings. Any access to this class or its fields should be done while
252 * holding the JobSchedulerService.mLock lock.
253 */
254 private final class Constants extends ContentObserver {
255 // Key names stored in the settings value.
256 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
257 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800258 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700259 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700260 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
261 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
262 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
263 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
264 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
265 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
266 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
267 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
268 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
269 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700270 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
271 = "max_standard_reschedule_count";
272 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
273 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
274 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700275 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
276 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
277 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
278 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700279
280 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
281 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800282 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700283 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700284 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
285 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
286 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
287 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
288 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
289 private static final int DEFAULT_FG_JOB_COUNT = 4;
290 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
291 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700292 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700293 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700294 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
295 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
296 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
297 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700298 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000299 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
300 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700301 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700302
303 /**
304 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
305 * early.
306 */
307 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
308 /**
309 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
310 * things early.
311 */
312 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
313 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800314 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
315 * schedule things early.
316 */
317 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
318 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700319 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
320 * schedule things early.
321 */
322 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
323 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700324 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
325 * things early. 1 == Run connectivity jobs as soon as ready.
326 */
327 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
328 /**
329 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
330 * schedule things early.
331 */
332 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
333 /**
334 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
335 * running some work early. This (and thus the other min counts) is now set to 1, to
336 * prevent any batching at this level. Since we now do batching through doze, that is
337 * a much better mechanism.
338 */
339 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
340 /**
341 * This is the job execution factor that is considered to be heavy use of the system.
342 */
343 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
344 /**
345 * This is the job execution factor that is considered to be moderate use of the system.
346 */
347 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
348 /**
349 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
350 */
351 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
352 /**
353 * The maximum number of background jobs we allow when the system is in a normal
354 * memory state.
355 */
356 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
357 /**
358 * The maximum number of background jobs we allow when the system is in a moderate
359 * memory state.
360 */
361 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
362 /**
363 * The maximum number of background jobs we allow when the system is in a low
364 * memory state.
365 */
366 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
367 /**
368 * The maximum number of background jobs we allow when the system is in a critical
369 * memory state.
370 */
371 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700372 /**
373 * The maximum number of times we allow a job to have itself rescheduled before
374 * giving up on it, for standard jobs.
375 */
376 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
377 /**
378 * The maximum number of times we allow a job to have itself rescheduled before
379 * giving up on it, for jobs that are executing work.
380 */
381 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
382 /**
383 * The minimum backoff time to allow for linear backoff.
384 */
385 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
386 /**
387 * The minimum backoff time to allow for exponential backoff.
388 */
389 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700390 /**
391 * How often we recalculate runnability based on apps' standby bucket assignment.
392 * This should be prime relative to common time interval lengths such as a quarter-
393 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
394 */
395 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
396
397 /**
398 * Mapping: standby bucket -> number of heartbeats between each sweep of that
399 * bucket's jobs.
400 *
401 * Bucket assignments as recorded in the JobStatus objects are normalized to be
402 * indices into this array, rather than the raw constants used
403 * by AppIdleHistory.
404 */
405 final int[] STANDBY_BEATS = {
406 0,
407 DEFAULT_STANDBY_WORKING_BEATS,
408 DEFAULT_STANDBY_FREQUENT_BEATS,
409 DEFAULT_STANDBY_RARE_BEATS
410 };
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700411
412 private ContentResolver mResolver;
413 private final KeyValueListParser mParser = new KeyValueListParser(',');
414
415 public Constants(Handler handler) {
416 super(handler);
417 }
418
419 public void start(ContentResolver resolver) {
420 mResolver = resolver;
421 mResolver.registerContentObserver(Settings.Global.getUriFor(
422 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
423 updateConstants();
424 }
425
426 @Override
427 public void onChange(boolean selfChange, Uri uri) {
428 updateConstants();
429 }
430
431 private void updateConstants() {
432 synchronized (mLock) {
433 try {
434 mParser.setString(Settings.Global.getString(mResolver,
Suprabh Shukla7a0a5c42017-07-25 21:07:13 +0000435 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700436 } catch (IllegalArgumentException e) {
437 // Failed to parse the settings string, log this and move on
438 // with defaults.
Suprabh Shukla7a0a5c42017-07-25 21:07:13 +0000439 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700440 }
441
442 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
443 DEFAULT_MIN_IDLE_COUNT);
444 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
445 DEFAULT_MIN_CHARGING_COUNT);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800446 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
447 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700448 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
449 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700450 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
451 DEFAULT_MIN_CONNECTIVITY_COUNT);
452 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
453 DEFAULT_MIN_CONTENT_COUNT);
454 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
455 DEFAULT_MIN_READY_JOBS_COUNT);
456 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
457 DEFAULT_HEAVY_USE_FACTOR);
458 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
459 DEFAULT_MODERATE_USE_FACTOR);
460 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
461 DEFAULT_FG_JOB_COUNT);
462 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
463 DEFAULT_BG_NORMAL_JOB_COUNT);
464 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
465 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
466 }
467 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
468 DEFAULT_BG_MODERATE_JOB_COUNT);
469 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
470 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
471 }
472 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
473 DEFAULT_BG_LOW_JOB_COUNT);
474 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
475 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
476 }
477 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
478 DEFAULT_BG_CRITICAL_JOB_COUNT);
479 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
480 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
481 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700482 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
483 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
484 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
485 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800486 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700487 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800488 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700489 DEFAULT_MIN_EXP_BACKOFF_TIME);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800490 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
Christopher Tatea732f012017-10-26 17:26:53 -0700491 DEFAULT_STANDBY_HEARTBEAT_TIME);
492 STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
493 DEFAULT_STANDBY_WORKING_BEATS);
494 STANDBY_BEATS[2] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
495 DEFAULT_STANDBY_FREQUENT_BEATS);
496 STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
497 DEFAULT_STANDBY_RARE_BEATS);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700498 }
499 }
500
501 void dump(PrintWriter pw) {
502 pw.println(" Settings:");
503
504 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
505 pw.print(MIN_IDLE_COUNT); pw.println();
506
507 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
508 pw.print(MIN_CHARGING_COUNT); pw.println();
509
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800510 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
511 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
512
Dianne Hackborn532ea262017-03-17 17:50:55 -0700513 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
514 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
515
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700516 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
517 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
518
519 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
520 pw.print(MIN_CONTENT_COUNT); pw.println();
521
522 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
523 pw.print(MIN_READY_JOBS_COUNT); pw.println();
524
525 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
526 pw.print(HEAVY_USE_FACTOR); pw.println();
527
528 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
529 pw.print(MODERATE_USE_FACTOR); pw.println();
530
531 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
532 pw.print(FG_JOB_COUNT); pw.println();
533
534 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
535 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
536
537 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
538 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
539
540 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
541 pw.print(BG_LOW_JOB_COUNT); pw.println();
542
543 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
544 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700545
546 pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
547 pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
548
549 pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
550 pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
551
552 pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
553 pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
554
555 pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
556 pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
Christopher Tatea732f012017-10-26 17:26:53 -0700557
558 pw.print(" "); pw.print(KEY_STANDBY_HEARTBEAT_TIME); pw.print("=");
559 pw.print(STANDBY_HEARTBEAT_TIME); pw.println();
560
561 pw.print(" standby_beats={");
562 pw.print(STANDBY_BEATS[0]);
563 for (int i = 1; i < STANDBY_BEATS.length; i++) {
564 pw.print(", ");
565 pw.print(STANDBY_BEATS[i]);
566 }
567 pw.println('}');
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700568 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800569
570 void dump(ProtoOutputStream proto, long fieldId) {
571 final long token = proto.start(fieldId);
572
573 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
574 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
575 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
576 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
577 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
578 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
579 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
580 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
581 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
582 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
583 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
584 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
585 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
586 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
587 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
588 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
589 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
590 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
591 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
592
593 for (int period : STANDBY_BEATS) {
594 proto.write(ConstantsProto.STANDBY_BEATS, period);
595 }
596
597 proto.end(token);
598 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700599 }
600
601 final Constants mConstants;
602
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700603 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
604 if (o1.enqueueTime < o2.enqueueTime) {
605 return -1;
606 }
607 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
608 };
609
610 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
611 int where = Collections.binarySearch(array, newItem, comparator);
612 if (where < 0) {
613 where = ~where;
614 }
615 array.add(where, newItem);
616 }
617
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700618 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700619 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
620 * still clean up. On reinstall the package will have a new uid.
621 */
622 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
623 @Override
624 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700625 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700626 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700627 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700628 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700629 final String pkgName = getPackageName(intent);
630 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
631
Christopher Tateee7805b2016-07-15 16:56:56 -0700632 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700633 // Purge the app's jobs if the whole package was just disabled. When this is
634 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700635 if (pkgName != null && pkgUid != -1) {
636 final String[] changedComponents = intent.getStringArrayExtra(
637 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
638 if (changedComponents != null) {
639 for (String component : changedComponents) {
640 if (component.equals(pkgName)) {
641 if (DEBUG) {
642 Slog.d(TAG, "Package state change: " + pkgName);
643 }
644 try {
645 final int userId = UserHandle.getUserId(pkgUid);
646 IPackageManager pm = AppGlobals.getPackageManager();
647 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
648 if (state == COMPONENT_ENABLED_STATE_DISABLED
649 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
650 if (DEBUG) {
651 Slog.d(TAG, "Removing jobs for package " + pkgName
652 + " in user " + userId);
653 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700654 cancelJobsForPackageAndUid(pkgName, pkgUid,
655 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700656 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700657 } catch (RemoteException|IllegalArgumentException e) {
658 /*
659 * IllegalArgumentException means that the package doesn't exist.
660 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
661 * behind outright uninstall, so by the time we try to act it's gone.
662 * We don't need to act on this PACKAGE_CHANGED when this happens;
663 * we'll get a PACKAGE_REMOVED later and clean up then.
664 *
665 * RemoteException can't actually happen; the package manager is
666 * running in this same process.
667 */
668 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700669 break;
670 }
671 }
672 }
673 } else {
674 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
675 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700676 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700677 // If this is an outright uninstall rather than the first half of an
678 // app update sequence, cancel the jobs associated with the app.
679 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
680 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
681 if (DEBUG) {
682 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
683 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700684 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Christopher Tate7060b042014-06-09 19:50:00 -0700685 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700686 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700687 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
688 if (DEBUG) {
689 Slog.d(TAG, "Removing jobs for user: " + userId);
690 }
691 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700692 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
693 // Has this package scheduled any jobs, such that we will take action
694 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700695 if (pkgUid != -1) {
696 List<JobStatus> jobsForUid;
697 synchronized (mLock) {
698 jobsForUid = mJobs.getJobsByUid(pkgUid);
699 }
700 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
701 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
702 if (DEBUG) {
703 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
704 + pkgUid + " has jobs");
705 }
706 setResultCode(Activity.RESULT_OK);
707 break;
708 }
709 }
710 }
711 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
712 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700713 if (pkgUid != -1) {
714 if (DEBUG) {
715 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
716 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700717 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700718 }
Christopher Tate7060b042014-06-09 19:50:00 -0700719 }
720 }
721 };
722
Christopher Tateb5c07882016-05-26 17:11:09 -0700723 private String getPackageName(Intent intent) {
724 Uri uri = intent.getData();
725 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
726 return pkg;
727 }
728
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700729 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700730 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800731 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700732 }
733
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700734 @Override public void onUidGone(int uid, boolean disabled) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800735 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800736 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700737 cancelJobsForUid(uid, "uid gone");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800738 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700739 synchronized (mLock) {
740 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
741 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700742 }
743
744 @Override public void onUidActive(int uid) throws RemoteException {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700745 synchronized (mLock) {
746 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
747 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700748 }
749
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700750 @Override public void onUidIdle(int uid, boolean disabled) {
Dianne Hackborne07641d2016-11-09 15:07:23 -0800751 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700752 cancelJobsForUid(uid, "app uid idle");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800753 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700754 synchronized (mLock) {
755 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
756 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700757 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700758
759 @Override public void onUidCachedChanged(int uid, boolean cached) {
760 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700761 };
762
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800763 public Object getLock() {
764 return mLock;
765 }
766
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700767 public JobStore getJobStore() {
768 return mJobs;
769 }
770
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700771 @Override
772 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700773 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
774 // Let's kick any outstanding jobs for this user.
775 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
776 }
777
778 @Override
779 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700780 // Let's kick any outstanding jobs for this user.
781 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
782 }
783
784 @Override
785 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700786 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700787 }
788
Makoto Onuki15407842018-01-19 14:23:11 -0800789 /**
790 * Return whether an UID is in the foreground or not.
791 */
792 private boolean isUidInForeground(int uid) {
793 synchronized (mLock) {
794 if (mUidPriorityOverride.get(uid, 0) > 0) {
795 return true;
796 }
797 }
798 // Note UID observer may not be called in time, so we always check with the AM.
799 return mActivityManagerInternal.getUidProcessState(uid)
800 <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
801 }
802
803 private final Predicate<Integer> mIsUidInForegroundPredicate = this::isUidInForeground;
804
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700805 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
806 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700807 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800808 if (ActivityManager.getService().isAppStartModeDisabled(uId,
809 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700810 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
811 + " -- package not allowed to start");
812 return JobScheduler.RESULT_FAILURE;
813 }
814 } catch (RemoteException e) {
815 }
Christopher Tatea732f012017-10-26 17:26:53 -0700816
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800817 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700818 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
819
820 if (work != null && toCancel != null) {
821 // Fast path: we are adding work to an existing job, and the JobInfo is not
822 // changing. We can just directly enqueue this work in to the job.
823 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -0800824
Dianne Hackborn342e6032017-04-13 18:04:31 -0700825 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -0800826
827 // If any of work item is enqueued when the source is in the foreground,
828 // exempt the entire job.
829 toCancel.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
830
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700831 return JobScheduler.RESULT_SUCCESS;
832 }
833 }
834
835 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -0800836
837 // Give exemption if the source is in the foreground just now.
838 // Note if it's a sync job, this method is called on the handler so it's not exactly
839 // the state when requestSync() was called, but that should be fine because of the
840 // 1 minute foreground grace period.
841 jobStatus.maybeAddForegroundExemption(mIsUidInForegroundPredicate);
842
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700843 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800844 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800845 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800846 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
847 Slog.w(TAG, "Too many jobs for uid " + uId);
848 throw new IllegalStateException("Apps may not schedule more than "
849 + MAX_JOBS_PER_APP + " distinct jobs");
850 }
851 }
852
Dianne Hackborna47223f2017-03-30 13:49:13 -0700853 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700854 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700855
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700856 if (toCancel != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700857 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700858 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700859 if (work != null) {
860 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700861 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700862 }
863 startTrackingJobLocked(jobStatus, toCancel);
Tej Singhd5747a62018-01-08 20:57:35 -0800864 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
865 uId, null, jobStatus.getBatteryName(), 2);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700866
867 // If the job is immediately ready to run, then we can just immediately
868 // put it in the pending list and try to schedule it. This is especially
869 // important for jobs with a 0 deadline constraint, since they will happen a fair
870 // amount, we want to handle them as quickly as possible, and semantically we want to
871 // make sure we have started holding the wake lock for the job before returning to
872 // the caller.
873 // If the job is not yet ready to run, there is nothing more to do -- we are
874 // now just waiting for one of its controllers to change state and schedule
875 // the job appropriately.
876 if (isReadyToBeExecutedLocked(jobStatus)) {
877 // This is a new job, we can just immediately put it on the pending
878 // list and try to run it.
879 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700880 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700881 maybeRunPendingJobsLocked();
882 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800883 }
Christopher Tate7060b042014-06-09 19:50:00 -0700884 return JobScheduler.RESULT_SUCCESS;
885 }
886
887 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800888 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800889 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
890 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
891 for (int i = jobs.size() - 1; i >= 0; i--) {
892 JobStatus job = jobs.get(i);
893 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700894 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800895 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700896 }
Christopher Tate7060b042014-06-09 19:50:00 -0700897 }
898
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600899 public JobInfo getPendingJob(int uid, int jobId) {
900 synchronized (mLock) {
901 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
902 for (int i = jobs.size() - 1; i >= 0; i--) {
903 JobStatus job = jobs.get(i);
904 if (job.getJobId() == jobId) {
905 return job.getJob();
906 }
907 }
908 return null;
909 }
910 }
911
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700912 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800913 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700914 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
915 for (int i=0; i<jobsForUser.size(); i++) {
916 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700917 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700918 }
Christopher Tate7060b042014-06-09 19:50:00 -0700919 }
920 }
921
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -0700922 private void cancelJobsForNonExistentUsers() {
923 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
924 synchronized (mLock) {
925 mJobs.removeJobsOfNonUsers(umi.getUserIds());
926 }
927 }
928
Makoto Onukie7b96182017-08-30 14:53:16 -0700929 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
930 if ("android".equals(pkgName)) {
931 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
932 return;
933 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700934 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700935 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
936 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
937 final JobStatus job = jobsForUid.get(i);
938 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700939 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700940 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700941 }
942 }
943 }
944
Christopher Tate7060b042014-06-09 19:50:00 -0700945 /**
946 * Entry point from client to cancel all jobs originating from their uid.
947 * This will remove the job from the master list, and cancel the job if it was staged for
948 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700949 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800950 *
Christopher Tate7060b042014-06-09 19:50:00 -0700951 */
Christopher Tate8c67d122017-09-29 16:54:26 -0700952 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -0700953 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700954 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -0700955 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -0700956 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700957
958 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800959 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700960 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
961 for (int i=0; i<jobsForUid.size(); i++) {
962 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700963 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -0700964 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700965 }
Christopher Tate7060b042014-06-09 19:50:00 -0700966 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700967 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -0700968 }
969
970 /**
971 * Entry point from client to cancel the job corresponding to the jobId provided.
972 * This will remove the job from the master list, and cancel the job if it was staged for
973 * execution or being executed.
974 * @param uid Uid of the calling client.
975 * @param jobId Id of the job, provided at schedule-time.
976 */
Makoto Onukid2bfec62018-01-12 13:58:01 -0800977 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -0700978 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800979 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700980 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700981 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -0800982 cancelJobImplLocked(toCancel, null,
983 "cancel() called by app, callingUid=" + callingUid
984 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700985 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700986 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -0700987 }
988 }
989
Dianne Hackborn729a3282017-06-09 16:06:01 -0700990 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700991 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
992 cancelled.unprepareLocked(ActivityManager.getService());
993 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
994 // Remove from pending queue.
995 if (mPendingJobs.remove(cancelled)) {
996 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -0700997 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700998 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -0700999 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001000 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001001 }
1002
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001003 void updateUidState(int uid, int procState) {
1004 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001005 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1006 // Only use this if we are exactly the top app. All others can live
1007 // with just the foreground priority. This means that persistent processes
1008 // can never be the top app priority... that is fine.
1009 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001010 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001011 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001012 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001013 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001014 }
1015 }
1016 }
1017
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001018 @Override
1019 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001020 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001021 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001022 // When becoming idle, make sure no jobs are actively running,
1023 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001024 for (int i=0; i<mActiveServices.size(); i++) {
1025 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001026 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001027 if (executing != null
1028 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001029 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1030 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001031 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001032 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001033 } else {
1034 // When coming out of idle, allow thing to start back up.
1035 if (mReadyToRock) {
1036 if (mLocalDeviceIdleController != null) {
1037 if (!mReportedActive) {
1038 mReportedActive = true;
1039 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001040 }
1041 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001042 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001043 }
1044 }
1045 }
1046 }
1047
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001048 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001049 // active is true if pending queue contains jobs OR some job is running.
1050 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001051 if (mPendingJobs.size() <= 0) {
1052 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001053 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001054 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001055 if (job != null
1056 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
1057 && !job.dozeWhitelisted) {
1058 // We will report active if we have a job running and it is not an exception
1059 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001060 active = true;
1061 break;
1062 }
1063 }
1064 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001065
1066 if (mReportedActive != active) {
1067 mReportedActive = active;
1068 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001069 mLocalDeviceIdleController.setJobsActive(active);
1070 }
1071 }
1072 }
1073
Christopher Tate7060b042014-06-09 19:50:00 -07001074 /**
1075 * Initializes the system service.
1076 * <p>
1077 * Subclasses must define a single argument constructor that accepts the context
1078 * and passes it to super.
1079 * </p>
1080 *
1081 * @param context The system server context.
1082 */
1083 public JobSchedulerService(Context context) {
1084 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001085
1086 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001087 mActivityManagerInternal = Preconditions.checkNotNull(
1088 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001089
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001090 mHandler = new JobHandler(context.getMainLooper());
1091 mConstants = new Constants(mHandler);
1092 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001093
1094 // Set up the app standby bucketing tracker
1095 UsageStatsManagerInternal usageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1096 mStandbyTracker = new StandbyTracker(usageStats);
1097 usageStats.addAppIdleStateChangeListener(mStandbyTracker);
1098
1099 // The job store needs to call back
1100 publishLocalService(JobSchedulerInternal.class, new LocalService());
1101
1102 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001103 mJobs = JobStore.initAndGet(this);
1104
Christopher Tate7060b042014-06-09 19:50:00 -07001105 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001106 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -07001107 mControllers.add(ConnectivityController.get(this));
1108 mControllers.add(TimeController.get(this));
1109 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001110 mBatteryController = BatteryController.get(this);
1111 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001112 mStorageController = StorageController.get(this);
1113 mControllers.add(mStorageController);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001114 mControllers.add(BackgroundJobsController.get(this));
Amith Yamasanib0ff3222015-03-04 09:56:14 -08001115 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001116 mControllers.add(ContentObserverController.get(this));
Suprabh Shukla106203b2017-11-02 21:23:44 -07001117 mDeviceIdleJobsController = DeviceIdleJobsController.get(this);
1118 mControllers.add(mDeviceIdleJobsController);
Christopher Tate616541d2017-07-26 14:27:38 -07001119
Makoto Onuki15407842018-01-19 14:23:11 -08001120 mForceAppStandbyTracker = ForceAppStandbyTracker.getInstance(context);
1121
Christopher Tate616541d2017-07-26 14:27:38 -07001122 // If the job store determined that it can't yet reschedule persisted jobs,
1123 // we need to start watching the clock.
1124 if (!mJobs.jobTimesInflatedValid()) {
1125 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1126 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1127 }
Christopher Tate7060b042014-06-09 19:50:00 -07001128 }
1129
Christopher Tate616541d2017-07-26 14:27:38 -07001130 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1131 @Override
1132 public void onReceive(Context context, Intent intent) {
1133 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1134 // When we reach clock sanity, recalculate the temporal windows
1135 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001136 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001137 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1138
1139 // We've done our job now, so stop watching the time.
1140 context.unregisterReceiver(this);
1141
1142 // And kick off the work to update the affected jobs, using a secondary
1143 // thread instead of chugging away here on the main looper thread.
1144 FgThread.getHandler().post(mJobTimeUpdater);
1145 }
1146 }
1147 }
1148 };
1149
1150 private final Runnable mJobTimeUpdater = () -> {
1151 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1152 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1153 synchronized (mLock) {
1154 // Note: we intentionally both look up the existing affected jobs and replace them
1155 // with recalculated ones inside the same lock lifetime.
1156 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1157
1158 // Now, at each position [i], we have both the existing JobStatus
1159 // and the one that replaces it.
1160 final int N = toAdd.size();
1161 for (int i = 0; i < N; i++) {
1162 final JobStatus oldJob = toRemove.get(i);
1163 final JobStatus newJob = toAdd.get(i);
1164 if (DEBUG) {
1165 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1166 }
1167 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1168 }
1169 }
1170 };
1171
Christopher Tate7060b042014-06-09 19:50:00 -07001172 @Override
1173 public void onStart() {
1174 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1175 }
1176
1177 @Override
1178 public void onBootPhase(int phase) {
1179 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001180 mConstants.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001181
1182 mForceAppStandbyTracker.start();
1183
Shreyas Basarge5db09082016-01-07 13:38:29 +00001184 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001185 final IntentFilter filter = new IntentFilter();
1186 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1187 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001188 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1189 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001190 filter.addDataScheme("package");
1191 getContext().registerReceiverAsUser(
1192 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1193 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1194 getContext().registerReceiverAsUser(
1195 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001196 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001197 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001198 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001199 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1200 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001201 } catch (RemoteException e) {
1202 // ignored; both services live in system_server
1203 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001204 // Remove any jobs that are not associated with any of the current users.
1205 cancelJobsForNonExistentUsers();
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001206 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001207 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001208 // Let's go!
1209 mReadyToRock = true;
1210 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1211 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001212 mLocalDeviceIdleController
1213 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001214 // Create the "runners".
1215 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1216 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001217 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001218 getContext().getMainLooper()));
1219 }
1220 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -08001221 mJobs.forEachJob(new JobStatusFunctor() {
1222 @Override
1223 public void process(JobStatus job) {
1224 for (int controller = 0; controller < mControllers.size(); controller++) {
1225 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001226 sc.maybeStartTrackingJobLocked(job, null);
1227 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001228 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001229 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001230 // GO GO GO!
1231 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1232 }
Christopher Tate7060b042014-06-09 19:50:00 -07001233 }
1234 }
1235
1236 /**
1237 * Called when we have a job status object that we need to insert in our
1238 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1239 * about.
1240 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001241 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1242 if (!jobStatus.isPreparedLocked()) {
1243 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1244 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001245 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001246 final boolean update = mJobs.add(jobStatus);
1247 if (mReadyToRock) {
1248 for (int i = 0; i < mControllers.size(); i++) {
1249 StateController controller = mControllers.get(i);
1250 if (update) {
1251 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001252 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001253 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001254 }
Christopher Tate7060b042014-06-09 19:50:00 -07001255 }
1256 }
1257
1258 /**
1259 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1260 * object removed.
1261 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001262 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001263 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001264 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001265 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001266
1267 // Remove from store as well as controllers.
1268 final boolean removed = mJobs.remove(jobStatus, writeBack);
1269 if (removed && mReadyToRock) {
1270 for (int i=0; i<mControllers.size(); i++) {
1271 StateController controller = mControllers.get(i);
1272 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001273 }
1274 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001275 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001276 }
1277
Dianne Hackborn729a3282017-06-09 16:06:01 -07001278 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001279 for (int i=0; i<mActiveServices.size(); i++) {
1280 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001281 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001282 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001283 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001284 return true;
1285 }
1286 }
1287 return false;
1288 }
1289
1290 /**
1291 * @param job JobStatus we are querying against.
1292 * @return Whether or not the job represented by the status object is currently being run or
1293 * is pending.
1294 */
1295 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001296 for (int i=0; i<mActiveServices.size(); i++) {
1297 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001298 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001299 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1300 return true;
1301 }
1302 }
1303 return false;
1304 }
1305
Dianne Hackborn807de782016-04-07 17:54:41 -07001306 void noteJobsPending(List<JobStatus> jobs) {
1307 for (int i = jobs.size() - 1; i >= 0; i--) {
1308 JobStatus job = jobs.get(i);
1309 mJobPackageTracker.notePending(job);
1310 }
1311 }
1312
1313 void noteJobsNonpending(List<JobStatus> jobs) {
1314 for (int i = jobs.size() - 1; i >= 0; i--) {
1315 JobStatus job = jobs.get(i);
1316 mJobPackageTracker.noteNonpending(job);
1317 }
1318 }
1319
Christopher Tate7060b042014-06-09 19:50:00 -07001320 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001321 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1322 * specify an override deadline on a failed job (the failed job will run even though it's not
1323 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1324 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1325 *
Christopher Tate7060b042014-06-09 19:50:00 -07001326 * @param failureToReschedule Provided job status that we will reschedule.
1327 * @return A newly instantiated JobStatus with the same constraints as the last job except
1328 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001329 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001330 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001331 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001332 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001333 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001334 final JobInfo job = failureToReschedule.getJob();
1335
1336 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001337 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1338 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001339
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001340 if (failureToReschedule.hasWorkLocked()) {
1341 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1342 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1343 + backoffAttempts + " > work limit "
1344 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1345 return null;
1346 }
1347 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1348 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1349 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1350 return null;
1351 }
1352
Christopher Tate7060b042014-06-09 19:50:00 -07001353 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001354 case JobInfo.BACKOFF_POLICY_LINEAR: {
1355 long backoff = initialBackoffMillis;
1356 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1357 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1358 }
1359 delayMillis = backoff * backoffAttempts;
1360 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001361 default:
1362 if (DEBUG) {
1363 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1364 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001365 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1366 long backoff = initialBackoffMillis;
1367 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1368 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1369 }
1370 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1371 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001372 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001373 delayMillis =
1374 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001375 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1376 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001377 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001378 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001379 for (int ic=0; ic<mControllers.size(); ic++) {
1380 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001381 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001382 }
1383 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001384 }
1385
1386 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001387 * Called after a periodic has executed so we can reschedule it. We take the last execution
1388 * time of the job to be the time of completion (i.e. the time at which this function is
1389 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001390 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001391 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1392 * to underscheduling at least, rather than if we had taken the last execution time to be the
1393 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001394 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1395 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001396 * @return A new job representing the execution criteria for this instantiation of the
1397 * recurring job.
1398 */
1399 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001400 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001401 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001402 long runEarly = 0L;
1403
1404 // If this periodic was rescheduled it won't have a deadline.
1405 if (periodicToReschedule.hasDeadlineConstraint()) {
1406 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1407 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001408 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001409 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001410 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1411 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001412
1413 if (DEBUG) {
1414 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1415 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1416 }
Christopher Tatea732f012017-10-26 17:26:53 -07001417 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1418 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1419 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001420 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001421 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001422 }
1423
1424 // JobCompletedListener implementations.
1425
1426 /**
1427 * A job just finished executing. We fetch the
1428 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1429 * whether we want to reschedule we readd it to the controllers.
1430 * @param jobStatus Completed job.
1431 * @param needsReschedule Whether the implementing class should reschedule this job.
1432 */
1433 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001434 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001435 if (DEBUG) {
1436 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1437 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001438
1439 // If the job wants to be rescheduled, we first need to make the next upcoming
1440 // job so we can transfer any appropriate state over from the previous job when
1441 // we stop it.
1442 final JobStatus rescheduledJob = needsReschedule
1443 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1444
Shreyas Basarge73f10252016-02-11 17:06:13 +00001445 // Do not write back immediately if this is a periodic job. The job may get lost if system
1446 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001447 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001448 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001449 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001450 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001451 // We still want to check for jobs to execute, because this job may have
1452 // scheduled a new job under the same job id, and now we can run it.
1453 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001454 return;
1455 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001456
1457 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001458 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001459 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001460 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001461 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001462 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001463 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001464 } else if (jobStatus.getJob().isPeriodic()) {
1465 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001466 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001467 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001468 } catch (SecurityException e) {
1469 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1470 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001471 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001472 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001473 jobStatus.unprepareLocked(ActivityManager.getService());
1474 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001475 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001476 }
1477
1478 // StateChangedListener implementations.
1479
1480 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001481 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1482 * some controller's state has changed, so as to run through the list of jobs and start/stop
1483 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001484 */
1485 @Override
1486 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001487 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001488 }
1489
1490 @Override
1491 public void onRunJobNow(JobStatus jobStatus) {
1492 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1493 }
1494
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001495 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001496
1497 public JobHandler(Looper looper) {
1498 super(looper);
1499 }
1500
1501 @Override
1502 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001503 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001504 if (!mReadyToRock) {
1505 return;
1506 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001507 switch (message.what) {
1508 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001509 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001510 // runNow can be null, which is a controller's way of indicating that its
1511 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001512 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001513 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001514 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001515 } else {
1516 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001517 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001518 } break;
1519 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001520 if (mReportedActive) {
1521 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001522 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001523 } else {
1524 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001525 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001526 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001527 break;
1528 case MSG_CHECK_JOB_GREEDY:
1529 queueReadyJobsForExecutionLocked();
1530 break;
1531 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001532 cancelJobImplLocked((JobStatus) message.obj, null,
1533 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001534 break;
Matthew Williams75fc5252014-09-02 16:17:53 -07001535 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001536 maybeRunPendingJobsLocked();
1537 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1538 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001539 }
1540 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001541 }
Christopher Tate7060b042014-06-09 19:50:00 -07001542
Dianne Hackborn6d068262017-05-16 13:14:37 -07001543 private void stopNonReadyActiveJobsLocked() {
1544 for (int i=0; i<mActiveServices.size(); i++) {
1545 JobServiceContext serviceContext = mActiveServices.get(i);
1546 final JobStatus running = serviceContext.getRunningJobLocked();
1547 if (running != null && !running.isReady()) {
1548 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001549 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1550 "cancelled due to unsatisfied constraints");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001551 }
1552 }
1553 }
1554
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001555 /**
1556 * Run through list of jobs and execute all possible - at least one is expired so we do
1557 * as many as we can.
1558 */
1559 private void queueReadyJobsForExecutionLocked() {
1560 if (DEBUG) {
1561 Slog.d(TAG, "queuing all ready jobs for execution:");
1562 }
1563 noteJobsNonpending(mPendingJobs);
1564 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001565 stopNonReadyActiveJobsLocked();
Christopher Tatea732f012017-10-26 17:26:53 -07001566 boolean updated = updateStandbyHeartbeatLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001567 mJobs.forEachJob(mReadyQueueFunctor);
Christopher Tatea732f012017-10-26 17:26:53 -07001568 if (updated) updateNextStandbyHeartbeatsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001569 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001570
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001571 if (DEBUG) {
1572 final int queuedJobs = mPendingJobs.size();
1573 if (queuedJobs == 0) {
1574 Slog.d(TAG, "No jobs pending.");
1575 } else {
1576 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001577 }
1578 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001579 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001580
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001581 final class ReadyJobQueueFunctor implements JobStatusFunctor {
1582 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001583
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001584 @Override
1585 public void process(JobStatus job) {
1586 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001587 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001588 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001589 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001590 if (newReadyJobs == null) {
1591 newReadyJobs = new ArrayList<JobStatus>();
1592 }
1593 newReadyJobs.add(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001594 }
1595 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001596
1597 public void postProcess() {
1598 if (newReadyJobs != null) {
1599 noteJobsPending(newReadyJobs);
1600 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001601 if (mPendingJobs.size() > 1) {
1602 mPendingJobs.sort(mEnqueueTimeComparator);
1603 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001604 }
1605 newReadyJobs = null;
1606 }
1607 }
1608 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1609
1610 /**
1611 * The state of at least one job has changed. Here is where we could enforce various
1612 * policies on when we want to execute jobs.
1613 * Right now the policy is such:
1614 * If >1 of the ready jobs is idle mode we send all of them off
1615 * if more than 2 network connectivity jobs are ready we send them all off.
1616 * If more than 4 jobs total are ready we send them all off.
1617 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1618 */
1619 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1620 int chargingCount;
1621 int batteryNotLowCount;
1622 int storageNotLowCount;
1623 int idleCount;
1624 int backoffCount;
1625 int connectivityCount;
1626 int contentCount;
1627 List<JobStatus> runnableJobs;
1628
1629 public MaybeReadyJobQueueFunctor() {
1630 reset();
1631 }
1632
1633 // Functor method invoked for each job via JobStore.forEachJob()
1634 @Override
1635 public void process(JobStatus job) {
1636 if (isReadyToBeExecutedLocked(job)) {
1637 try {
1638 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1639 job.getJob().getService().getPackageName())) {
1640 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1641 + job.getJob().toString() + " -- package not allowed to start");
1642 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1643 return;
1644 }
1645 } catch (RemoteException e) {
1646 }
1647 if (job.getNumFailures() > 0) {
1648 backoffCount++;
1649 }
1650 if (job.hasIdleConstraint()) {
1651 idleCount++;
1652 }
1653 if (job.hasConnectivityConstraint()) {
1654 connectivityCount++;
1655 }
1656 if (job.hasChargingConstraint()) {
1657 chargingCount++;
1658 }
1659 if (job.hasBatteryNotLowConstraint()) {
1660 batteryNotLowCount++;
1661 }
1662 if (job.hasStorageNotLowConstraint()) {
1663 storageNotLowCount++;
1664 }
1665 if (job.hasContentTriggerConstraint()) {
1666 contentCount++;
1667 }
1668 if (runnableJobs == null) {
1669 runnableJobs = new ArrayList<>();
1670 }
1671 runnableJobs.add(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001672 }
1673 }
1674
1675 public void postProcess() {
1676 if (backoffCount > 0 ||
1677 idleCount >= mConstants.MIN_IDLE_COUNT ||
1678 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1679 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1680 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1681 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1682 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1683 (runnableJobs != null
1684 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1685 if (DEBUG) {
1686 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1687 }
1688 noteJobsPending(runnableJobs);
1689 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001690 if (mPendingJobs.size() > 1) {
1691 mPendingJobs.sort(mEnqueueTimeComparator);
1692 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001693 } else {
1694 if (DEBUG) {
1695 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1696 }
1697 }
1698
1699 // Be ready for next time
1700 reset();
1701 }
1702
1703 private void reset() {
1704 chargingCount = 0;
1705 idleCount = 0;
1706 backoffCount = 0;
1707 connectivityCount = 0;
1708 batteryNotLowCount = 0;
1709 storageNotLowCount = 0;
1710 contentCount = 0;
1711 runnableJobs = null;
1712 }
1713 }
1714 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1715
1716 private void maybeQueueReadyJobsForExecutionLocked() {
1717 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1718
1719 noteJobsNonpending(mPendingJobs);
1720 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001721 stopNonReadyActiveJobsLocked();
Christopher Tatea732f012017-10-26 17:26:53 -07001722 boolean updated = updateStandbyHeartbeatLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001723 mJobs.forEachJob(mMaybeQueueFunctor);
Christopher Tatea732f012017-10-26 17:26:53 -07001724 if (updated) updateNextStandbyHeartbeatsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001725 mMaybeQueueFunctor.postProcess();
1726 }
1727
Christopher Tatea732f012017-10-26 17:26:53 -07001728 private boolean updateStandbyHeartbeatLocked() {
1729 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1730 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1731 if (beatsElapsed > 0) {
1732 mHeartbeat += beatsElapsed;
1733 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1734 if (DEBUG_STANDBY) {
1735 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed + " to " + mHeartbeat);
1736 }
1737 return true;
1738 }
1739 return false;
1740 }
1741
1742 private void updateNextStandbyHeartbeatsLocked() {
1743 // don't update ACTIVE or NEVER bucket milestones
1744 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
1745 while (mHeartbeat >= mNextBucketHeartbeat[i]) {
1746 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1747 }
1748 if (DEBUG_STANDBY) {
1749 Slog.v(TAG, " Bucket " + i + " next heartbeat " + mNextBucketHeartbeat[i]);
1750 }
1751 }
1752 }
1753
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001754 /**
1755 * Criteria for moving a job into the pending queue:
1756 * - It's ready.
1757 * - It's not pending.
1758 * - It's not already running on a JSC.
1759 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07001760 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001761 * - The component is enabled and runnable.
1762 */
1763 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001764 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001765
1766 if (DEBUG) {
1767 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1768 + " ready=" + jobReady);
1769 }
1770
1771 // This is a condition that is very likely to be false (most jobs that are
1772 // scheduled are sitting there, not ready yet) and very cheap to check (just
1773 // a few conditions on data in JobStatus).
1774 if (!jobReady) {
1775 return false;
1776 }
1777
1778 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001779
1780 final int userId = job.getUserId();
1781 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1782
1783 if (DEBUG) {
1784 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001785 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001786 }
1787
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001788 // These are also fairly cheap to check, though they typically will not
1789 // be conditions we fail.
1790 if (!jobExists || !userStarted) {
1791 return false;
1792 }
1793
1794 final boolean jobPending = mPendingJobs.contains(job);
1795 final boolean jobActive = isCurrentlyActiveLocked(job);
1796
1797 if (DEBUG) {
1798 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1799 + " pending=" + jobPending + " active=" + jobActive);
1800 }
1801
1802 // These can be a little more expensive (especially jobActive, since we need to
1803 // go through the array of all potentially active jobs), so we are doing them
1804 // later... but still before checking with the package manager!
1805 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001806 return false;
1807 }
1808
Christopher Tatea732f012017-10-26 17:26:53 -07001809 // If the app is in a non-active standby bucket, make sure we've waited
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001810 // an appropriate amount of time since the last invocation. During device-
1811 // wide parole, standby bucketing is ignored.
Makoto Onuki959acb52018-01-26 14:10:03 -08001812 //
1813 // But if a job has FLAG_EXEMPT_FROM_APP_STANDBY, don't check it.
1814 if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001815 final int bucket = job.getStandbyBucket();
1816 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
1817 // Only skip this job if it's still waiting for the end of its (initial) nominal
1818 // bucket interval. Once it's waited that long, we let it go ahead and clear.
1819 // The final (NEVER) bucket is special; we never age those apps' jobs into
1820 // runnability.
1821 if (bucket >= mConstants.STANDBY_BEATS.length
1822 || (mHeartbeat < job.getBaseHeartbeat() + mConstants.STANDBY_BEATS[bucket])) {
1823 // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1824 if (job.getWhenStandbyDeferred() == 0) {
1825 if (DEBUG_STANDBY) {
1826 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
1827 + mNextBucketHeartbeat[job.getStandbyBucket()] + " for " + job);
1828 }
1829 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tate0c4d7682017-12-06 15:10:22 -08001830 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001831 return false;
1832 } else {
1833 if (DEBUG_STANDBY) {
1834 Slog.v(TAG, "Bucket deferred job aged into runnability at "
1835 + mHeartbeat + " : " + job);
1836 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08001837 }
Christopher Tatea732f012017-10-26 17:26:53 -07001838 }
Christopher Tatea732f012017-10-26 17:26:53 -07001839 }
1840
1841 // The expensive check last: validate that the defined package+service is
1842 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001843 final boolean componentPresent;
1844 try {
1845 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1846 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1847 userId) != null);
1848 } catch (RemoteException e) {
1849 throw e.rethrowAsRuntimeException();
1850 }
1851
1852 if (DEBUG) {
1853 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1854 + " componentPresent=" + componentPresent);
1855 }
1856
1857 // Everything else checked out so far, so this is the final yes/no check
1858 return componentPresent;
1859 }
1860
1861 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001862 * Reconcile jobs in the pending queue against available execution contexts.
1863 * A controller can force a job into the pending queue even if it's already running, but
1864 * here is where we decide whether to actually execute it.
1865 */
1866 private void maybeRunPendingJobsLocked() {
1867 if (DEBUG) {
1868 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1869 }
1870 assignJobsToContextsLocked();
1871 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001872 }
1873
Dianne Hackborn807de782016-04-07 17:54:41 -07001874 private int adjustJobPriority(int curPriority, JobStatus job) {
1875 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1876 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001877 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001878 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001879 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001880 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1881 }
1882 }
1883 return curPriority;
1884 }
1885
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001886 private int evaluateJobPriorityLocked(JobStatus job) {
1887 int priority = job.getPriority();
1888 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001889 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001890 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001891 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1892 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001893 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001894 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001895 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001896 }
1897
Christopher Tate7060b042014-06-09 19:50:00 -07001898 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001899 * Takes jobs from pending queue and runs them on available contexts.
1900 * If no contexts are available, preempts lower priority jobs to
1901 * run higher priority ones.
1902 * Lock on mJobs before calling this function.
1903 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001904 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001905 if (DEBUG) {
1906 Slog.d(TAG, printPendingQueue());
1907 }
1908
Dianne Hackborn970510b2016-02-24 16:56:42 -08001909 int memLevel;
1910 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001911 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001912 } catch (RemoteException e) {
1913 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1914 }
1915 switch (memLevel) {
1916 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001917 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001918 break;
1919 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001920 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001921 break;
1922 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001923 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001924 break;
1925 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001926 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001927 break;
1928 }
1929
1930 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1931 boolean[] act = mTmpAssignAct;
1932 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
1933 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001934 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001935 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
1936 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001937 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001938 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001939 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001940 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
1941 numForeground++;
1942 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001943 }
1944 act[i] = false;
1945 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00001946 }
1947 if (DEBUG) {
1948 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
1949 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001950 for (int i=0; i<mPendingJobs.size(); i++) {
1951 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00001952
1953 // If job is already running, go to next job.
1954 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
1955 if (jobRunningContext != -1) {
1956 continue;
1957 }
1958
Dianne Hackborn970510b2016-02-24 16:56:42 -08001959 final int priority = evaluateJobPriorityLocked(nextPending);
1960 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001961
Shreyas Basarge5db09082016-01-07 13:38:29 +00001962 // Find a context for nextPending. The context should be available OR
1963 // it should have lowest priority among all running jobs
1964 // (sharing the same Uid as nextPending)
1965 int minPriority = Integer.MAX_VALUE;
1966 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001967 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
1968 JobStatus job = contextIdToJobMap[j];
1969 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00001970 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001971 if ((numActive < mMaxActiveJobs ||
1972 (priority >= JobInfo.PRIORITY_TOP_APP &&
1973 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08001974 (preferredUid == nextPending.getUid() ||
1975 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
1976 // This slot is free, and we haven't yet hit the limit on
1977 // concurrent jobs... we can just throw the job in to here.
1978 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001979 break;
1980 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00001981 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08001982 // the context has a preferred Uid or we have reached the limit on
1983 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00001984 continue;
1985 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00001986 if (job.getUid() != nextPending.getUid()) {
1987 continue;
1988 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001989 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001990 continue;
1991 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001992 if (minPriority > nextPending.lastEvaluatedPriority) {
1993 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001994 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00001995 }
1996 }
1997 if (minPriorityContextId != -1) {
1998 contextIdToJobMap[minPriorityContextId] = nextPending;
1999 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002000 numActive++;
2001 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2002 numForeground++;
2003 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002004 }
2005 }
2006 if (DEBUG) {
2007 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2008 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002009 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002010 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002011 boolean preservePreferredUid = false;
2012 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002013 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002014 if (js != null) {
2015 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002016 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00002017 }
2018 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07002019 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002020 preservePreferredUid = true;
2021 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002022 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00002023 if (DEBUG) {
2024 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002025 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002026 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002027 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002028 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002029 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002030 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2031 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002032 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002033 if (mPendingJobs.remove(pendingJob)) {
2034 mJobPackageTracker.noteNonpending(pendingJob);
2035 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002036 }
2037 }
2038 if (!preservePreferredUid) {
2039 mActiveServices.get(i).clearPreferredUid();
2040 }
2041 }
2042 }
2043
2044 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2045 for (int i=0; i<map.length; i++) {
2046 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2047 return i;
2048 }
2049 }
2050 return -1;
2051 }
2052
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002053 final class LocalService implements JobSchedulerInternal {
2054
2055 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002056 * The current bucket heartbeat ordinal
2057 */
2058 public long currentHeartbeat() {
2059 return getCurrentHeartbeat();
2060 }
2061
2062 /**
2063 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2064 */
2065 public long nextHeartbeatForBucket(int bucket) {
2066 synchronized (mLock) {
2067 return mNextBucketHeartbeat[bucket];
2068 }
2069 }
2070
2071 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002072 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2073 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2074 * jobs in a long time is immediately runnable even if the app is bucketed into
2075 * an infrequent time allocation.
2076 */
2077 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2078 final int appStandbyBucket) {
2079 if (appStandbyBucket == 0 ||
2080 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2081 // ACTIVE => everything can be run right away
2082 // NEVER => we won't run them anyway, so let them go in the future
2083 // as soon as the app enters normal use
2084 return 0;
2085 }
2086
2087 final long timeSinceLastJob = mStandbyTracker.getTimeSinceLastJobRun(
2088 packageName, userId);
2089 final long bucketLength = mConstants.STANDBY_BEATS[appStandbyBucket];
2090 final long bucketsAgo = timeSinceLastJob / bucketLength;
2091
2092 // If we haven't run any jobs for more than the app's current bucket period, just
2093 // consider anything new to be immediately runnable. Otherwise, base it on the
2094 // bucket at which we last ran jobs.
2095 return (bucketsAgo > bucketLength) ? 0 : (getCurrentHeartbeat() - bucketsAgo);
2096 }
2097
2098 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002099 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2100 * jobs are always considered pending.
2101 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002102 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002103 public List<JobInfo> getSystemScheduledPendingJobs() {
2104 synchronized (mLock) {
2105 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
2106 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
2107 @Override
2108 public void process(JobStatus job) {
2109 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2110 pendingJobs.add(job.getJob());
2111 }
2112 }
2113 });
2114 return pendingJobs;
2115 }
2116 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002117
2118 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002119 public void cancelJobsForUid(int uid, String reason) {
2120 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2121 }
2122
2123 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002124 public void addBackingUpUid(int uid) {
2125 synchronized (mLock) {
2126 // No need to actually do anything here, since for a full backup the
2127 // activity manager will kill the process which will kill the job (and
2128 // cause it to restart, but now it can't run).
2129 mBackingUpUids.put(uid, uid);
2130 }
2131 }
2132
2133 @Override
2134 public void removeBackingUpUid(int uid) {
2135 synchronized (mLock) {
2136 mBackingUpUids.delete(uid);
2137 // If there are any jobs for this uid, we need to rebuild the pending list
2138 // in case they are now ready to run.
2139 if (mJobs.countJobsForUid(uid) > 0) {
2140 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2141 }
2142 }
2143 }
2144
2145 @Override
2146 public void clearAllBackingUpUids() {
2147 synchronized (mLock) {
2148 if (mBackingUpUids.size() > 0) {
2149 mBackingUpUids.clear();
2150 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2151 }
2152 }
2153 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002154
2155 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002156 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002157 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002158 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002159 }
2160 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002161 }
2162
Shreyas Basarge5db09082016-01-07 13:38:29 +00002163 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002164 * Tracking of app assignments to standby buckets
2165 */
2166 final class StandbyTracker extends AppIdleStateChangeListener {
2167 final UsageStatsManagerInternal mUsageStats;
2168
2169 StandbyTracker(UsageStatsManagerInternal usageStats) {
2170 mUsageStats = usageStats;
2171 }
2172
Christopher Tate435c2f42018-01-18 12:59:15 -08002173 public long getTimeSinceLastJobRun(String packageName, final @UserIdInt int userId) {
2174 return mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2175 }
2176
Christopher Tatea732f012017-10-26 17:26:53 -07002177 // AppIdleStateChangeListener interface for live updates
2178
2179 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002180 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Christopher Tatea732f012017-10-26 17:26:53 -07002181 boolean idle, int bucket) {
2182 final int uid = mLocalPM.getPackageUid(packageName,
2183 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2184 if (uid < 0) {
2185 if (DEBUG_STANDBY) {
2186 Slog.i(TAG, "App idle state change for unknown app "
2187 + packageName + "/" + userId);
2188 }
2189 return;
2190 }
2191
2192 final int bucketIndex = standbyBucketToBucketIndex(bucket);
2193 // update job bookkeeping out of band
2194 BackgroundThread.getHandler().post(() -> {
2195 if (DEBUG_STANDBY) {
2196 Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2197 }
2198 synchronized (mLock) {
2199 // TODO: update to be more efficient once we can slice by source UID
2200 mJobs.forEachJob((JobStatus job) -> {
2201 if (job.getSourceUid() == uid) {
2202 job.setStandbyBucket(bucketIndex);
2203 }
2204 });
2205 onControllerStateChanged();
2206 }
2207 });
2208 }
2209
2210 @Override
2211 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002212 if (DEBUG_STANDBY) {
2213 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2214 }
2215 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002216 }
2217 }
2218
2219 public static int standbyBucketToBucketIndex(int bucket) {
2220 // Normalize AppStandby constants to indices into our bookkeeping
Amith Yamasaniafbccb72017-11-27 10:44:24 -08002221 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
2222 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
2223 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
2224 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
Christopher Tatea732f012017-10-26 17:26:53 -07002225 else return 0;
2226 }
2227
2228 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2229 UsageStatsManagerInternal usageStats = LocalServices.getService(
2230 UsageStatsManagerInternal.class);
2231 int bucket = usageStats != null
2232 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2233 : 0;
2234
2235 bucket = standbyBucketToBucketIndex(bucket);
2236
2237 if (DEBUG_STANDBY) {
2238 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2239 }
2240 return bucket;
2241 }
2242
2243 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002244 * Binder stub trampoline implementation
2245 */
2246 final class JobSchedulerStub extends IJobScheduler.Stub {
2247 /** Cache determination of whether a given app can persist jobs
2248 * key is uid of the calling app; value is undetermined/true/false
2249 */
2250 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2251
2252 // Enforce that only the app itself (or shared uid participant) can schedule a
2253 // job that runs one of the app's services, as well as verifying that the
2254 // named service properly requires the BIND_JOB_SERVICE permission
2255 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002256 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002257 final ComponentName service = job.getService();
2258 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002259 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002260 PackageManager.MATCH_DIRECT_BOOT_AWARE
2261 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002262 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002263 if (si == null) {
2264 throw new IllegalArgumentException("No such service " + service);
2265 }
Christopher Tate7060b042014-06-09 19:50:00 -07002266 if (si.applicationInfo.uid != uid) {
2267 throw new IllegalArgumentException("uid " + uid +
2268 " cannot schedule job in " + service.getPackageName());
2269 }
2270 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2271 throw new IllegalArgumentException("Scheduled service " + service
2272 + " does not require android.permission.BIND_JOB_SERVICE permission");
2273 }
Christopher Tate5568f542014-06-18 13:53:31 -07002274 } catch (RemoteException e) {
2275 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002276 }
2277 }
2278
2279 private boolean canPersistJobs(int pid, int uid) {
2280 // If we get this far we're good to go; all we need to do now is check
2281 // whether the app is allowed to persist its scheduled work.
2282 final boolean canPersist;
2283 synchronized (mPersistCache) {
2284 Boolean cached = mPersistCache.get(uid);
2285 if (cached != null) {
2286 canPersist = cached.booleanValue();
2287 } else {
2288 // Persisting jobs is tantamount to running at boot, so we permit
2289 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2290 // permission
2291 int result = getContext().checkPermission(
2292 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2293 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2294 mPersistCache.put(uid, canPersist);
2295 }
2296 }
2297 return canPersist;
2298 }
2299
Makoto Onuki959acb52018-01-26 14:10:03 -08002300 private void validateJobFlags(JobInfo job, int callingUid) {
2301 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2302 getContext().enforceCallingOrSelfPermission(
2303 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2304 }
2305 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2306 if (callingUid != Process.SYSTEM_UID) {
2307 throw new SecurityException("Job has invalid flags");
2308 }
2309 if (job.hasLateConstraint() || job.hasEarlyConstraint()) {
2310 Slog.wtf(TAG, "Jobs with time-constraints mustn't have"
2311 +" FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
2312 }
2313 }
2314 }
2315
Christopher Tate7060b042014-06-09 19:50:00 -07002316 // IJobScheduler implementation
2317 @Override
2318 public int schedule(JobInfo job) throws RemoteException {
2319 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002320 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002321 }
2322 final int pid = Binder.getCallingPid();
2323 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002324 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002325
2326 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002327 if (job.isPersisted()) {
2328 if (!canPersistJobs(pid, uid)) {
2329 throw new IllegalArgumentException("Error: requested job be persisted without"
2330 + " holding RECEIVE_BOOT_COMPLETED permission.");
2331 }
2332 }
Christopher Tate7060b042014-06-09 19:50:00 -07002333
Makoto Onuki959acb52018-01-26 14:10:03 -08002334 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002335
Christopher Tate7060b042014-06-09 19:50:00 -07002336 long ident = Binder.clearCallingIdentity();
2337 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002338 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2339 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002340 } finally {
2341 Binder.restoreCallingIdentity(ident);
2342 }
2343 }
2344
2345 // IJobScheduler implementation
2346 @Override
2347 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2348 if (DEBUG) {
2349 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2350 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002351 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002352 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002353
2354 enforceValidJobRequest(uid, job);
2355 if (job.isPersisted()) {
2356 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2357 }
2358 if (work == null) {
2359 throw new NullPointerException("work is null");
2360 }
2361
Makoto Onuki959acb52018-01-26 14:10:03 -08002362 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002363
2364 long ident = Binder.clearCallingIdentity();
2365 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002366 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2367 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002368 } finally {
2369 Binder.restoreCallingIdentity(ident);
2370 }
2371 }
2372
2373 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002374 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002375 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002376 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002377 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002378 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002379 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002380 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002381
2382 if (packageName == null) {
2383 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002384 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002385
2386 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2387 android.Manifest.permission.UPDATE_DEVICE_STATS);
2388 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2389 throw new SecurityException("Caller uid " + callerUid
2390 + " not permitted to schedule jobs for other apps");
2391 }
2392
Makoto Onuki959acb52018-01-26 14:10:03 -08002393 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002394
Shreyas Basarge968ac752016-01-11 23:09:26 +00002395 long ident = Binder.clearCallingIdentity();
2396 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002397 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002398 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002399 } finally {
2400 Binder.restoreCallingIdentity(ident);
2401 }
2402 }
2403
2404 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002405 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2406 final int uid = Binder.getCallingUid();
2407
2408 long ident = Binder.clearCallingIdentity();
2409 try {
2410 return JobSchedulerService.this.getPendingJobs(uid);
2411 } finally {
2412 Binder.restoreCallingIdentity(ident);
2413 }
2414 }
2415
2416 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002417 public JobInfo getPendingJob(int jobId) throws RemoteException {
2418 final int uid = Binder.getCallingUid();
2419
2420 long ident = Binder.clearCallingIdentity();
2421 try {
2422 return JobSchedulerService.this.getPendingJob(uid, jobId);
2423 } finally {
2424 Binder.restoreCallingIdentity(ident);
2425 }
2426 }
2427
2428 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002429 public void cancelAll() throws RemoteException {
2430 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002431 long ident = Binder.clearCallingIdentity();
2432 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002433 JobSchedulerService.this.cancelJobsForUid(uid,
2434 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002435 } finally {
2436 Binder.restoreCallingIdentity(ident);
2437 }
2438 }
2439
2440 @Override
2441 public void cancel(int jobId) throws RemoteException {
2442 final int uid = Binder.getCallingUid();
2443
2444 long ident = Binder.clearCallingIdentity();
2445 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002446 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002447 } finally {
2448 Binder.restoreCallingIdentity(ident);
2449 }
2450 }
2451
2452 /**
2453 * "dumpsys" infrastructure
2454 */
2455 @Override
2456 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002457 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002458
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002459 int filterUid = -1;
2460 boolean proto = false;
2461 if (!ArrayUtils.isEmpty(args)) {
2462 int opti = 0;
2463 while (opti < args.length) {
2464 String arg = args[opti];
2465 if ("-h".equals(arg)) {
2466 dumpHelp(pw);
2467 return;
2468 } else if ("-a".equals(arg)) {
2469 // Ignore, we always dump all.
2470 } else if ("--proto".equals(arg)) {
2471 proto = true;
2472 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2473 pw.println("Unknown option: " + arg);
2474 return;
2475 } else {
2476 break;
2477 }
2478 opti++;
2479 }
2480 if (opti < args.length) {
2481 String pkg = args[opti];
2482 try {
2483 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2484 PackageManager.MATCH_ANY_USER);
2485 } catch (NameNotFoundException ignored) {
2486 pw.println("Invalid package: " + pkg);
2487 return;
2488 }
2489 }
2490 }
2491
Christopher Tate7060b042014-06-09 19:50:00 -07002492 long identityToken = Binder.clearCallingIdentity();
2493 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002494 if (proto) {
2495 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2496 } else {
2497 JobSchedulerService.this.dumpInternal(pw, filterUid);
2498 }
Christopher Tate7060b042014-06-09 19:50:00 -07002499 } finally {
2500 Binder.restoreCallingIdentity(identityToken);
2501 }
2502 }
Christopher Tate5d346052016-03-08 12:56:08 -08002503
2504 @Override
2505 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002506 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002507 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002508 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002509 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002510 };
2511
Christopher Tate5d346052016-03-08 12:56:08 -08002512 // Shell command infrastructure: run the given job immediately
2513 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2514 if (DEBUG) {
2515 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2516 + " " + jobId + " f=" + force);
2517 }
2518
2519 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002520 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2521 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002522 if (uid < 0) {
2523 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2524 }
2525
2526 synchronized (mLock) {
2527 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2528 if (js == null) {
2529 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2530 }
2531
2532 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2533 if (!js.isConstraintsSatisfied()) {
2534 js.overrideState = 0;
2535 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2536 }
2537
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002538 queueReadyJobsForExecutionLocked();
2539 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002540 }
2541 } catch (RemoteException e) {
2542 // can't happen
2543 }
2544 return 0;
2545 }
2546
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002547 // Shell command infrastructure: immediately timeout currently executing jobs
2548 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2549 boolean hasJobId, int jobId) {
2550 if (DEBUG) {
2551 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2552 }
2553
2554 synchronized (mLock) {
2555 boolean foundSome = false;
2556 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002557 final JobServiceContext jc = mActiveServices.get(i);
2558 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002559 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002560 foundSome = true;
2561 pw.print("Timing out: ");
2562 js.printUniqueId(pw);
2563 pw.print(" ");
2564 pw.println(js.getServiceComponent().flattenToShortString());
2565 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002566 }
2567 if (!foundSome) {
2568 pw.println("No matching executing jobs found.");
2569 }
2570 }
2571 return 0;
2572 }
2573
Christopher Tate8c67d122017-09-29 16:54:26 -07002574 // Shell command infrastructure: cancel a scheduled job
2575 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2576 boolean hasJobId, int jobId) {
2577 if (DEBUG) {
2578 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2579 }
2580
2581 int pkgUid = -1;
2582 try {
2583 IPackageManager pm = AppGlobals.getPackageManager();
2584 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2585 } catch (RemoteException e) { /* can't happen */ }
2586
2587 if (pkgUid < 0) {
2588 pw.println("Package " + pkgName + " not found.");
2589 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2590 }
2591
2592 if (!hasJobId) {
2593 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2594 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2595 pw.println("No matching jobs found.");
2596 }
2597 } else {
2598 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002599 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002600 pw.println("No matching job found.");
2601 }
2602 }
2603
2604 return 0;
2605 }
2606
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002607 void setMonitorBattery(boolean enabled) {
2608 synchronized (mLock) {
2609 if (mBatteryController != null) {
2610 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2611 }
2612 }
2613 }
2614
2615 int getBatterySeq() {
2616 synchronized (mLock) {
2617 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2618 }
2619 }
2620
2621 boolean getBatteryCharging() {
2622 synchronized (mLock) {
2623 return mBatteryController != null
2624 ? mBatteryController.getTracker().isOnStablePower() : false;
2625 }
2626 }
2627
2628 boolean getBatteryNotLow() {
2629 synchronized (mLock) {
2630 return mBatteryController != null
2631 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2632 }
2633 }
2634
Dianne Hackborn532ea262017-03-17 17:50:55 -07002635 int getStorageSeq() {
2636 synchronized (mLock) {
2637 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2638 }
2639 }
2640
2641 boolean getStorageNotLow() {
2642 synchronized (mLock) {
2643 return mStorageController != null
2644 ? mStorageController.getTracker().isStorageNotLow() : false;
2645 }
2646 }
2647
Christopher Tatea732f012017-10-26 17:26:53 -07002648 long getCurrentHeartbeat() {
2649 synchronized (mLock) {
2650 return mHeartbeat;
2651 }
2652 }
2653
Dianne Hackborn6d068262017-05-16 13:14:37 -07002654 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2655 try {
2656 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2657 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2658 if (uid < 0) {
2659 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2660 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2661 }
2662
2663 synchronized (mLock) {
2664 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2665 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2666 if (js == null) {
2667 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2668 pw.print("/jid"); pw.print(jobId); pw.println(")");
2669 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2670 }
2671
2672 boolean printed = false;
2673 if (mPendingJobs.contains(js)) {
2674 pw.print("pending");
2675 printed = true;
2676 }
2677 if (isCurrentlyActiveLocked(js)) {
2678 if (printed) {
2679 pw.print(" ");
2680 }
2681 printed = true;
2682 pw.println("active");
2683 }
2684 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2685 if (printed) {
2686 pw.print(" ");
2687 }
2688 printed = true;
2689 pw.println("user-stopped");
2690 }
2691 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2692 if (printed) {
2693 pw.print(" ");
2694 }
2695 printed = true;
2696 pw.println("backing-up");
2697 }
2698 boolean componentPresent = false;
2699 try {
2700 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2701 js.getServiceComponent(),
2702 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2703 js.getUserId()) != null);
2704 } catch (RemoteException e) {
2705 }
2706 if (!componentPresent) {
2707 if (printed) {
2708 pw.print(" ");
2709 }
2710 printed = true;
2711 pw.println("no-component");
2712 }
2713 if (js.isReady()) {
2714 if (printed) {
2715 pw.print(" ");
2716 }
2717 printed = true;
2718 pw.println("ready");
2719 }
2720 if (!printed) {
2721 pw.print("waiting");
2722 }
2723 pw.println();
2724 }
2725 } catch (RemoteException e) {
2726 // can't happen
2727 }
2728 return 0;
2729 }
2730
Shreyas Basarge5db09082016-01-07 13:38:29 +00002731 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2732 StringBuilder s = new StringBuilder(initial + ": ");
2733 for (int i=0; i<map.length; i++) {
2734 s.append("(")
2735 .append(map[i] == null? -1: map[i].getJobId())
2736 .append(map[i] == null? -1: map[i].getUid())
2737 .append(")" );
2738 }
2739 return s.toString();
2740 }
2741
2742 private String printPendingQueue() {
2743 StringBuilder s = new StringBuilder("Pending queue: ");
2744 Iterator<JobStatus> it = mPendingJobs.iterator();
2745 while (it.hasNext()) {
2746 JobStatus js = it.next();
2747 s.append("(")
2748 .append(js.getJob().getId())
2749 .append(", ")
2750 .append(js.getUid())
2751 .append(") ");
2752 }
2753 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07002754 }
Christopher Tate7060b042014-06-09 19:50:00 -07002755
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002756 static void dumpHelp(PrintWriter pw) {
2757 pw.println("Job Scheduler (jobscheduler) dump options:");
2758 pw.println(" [-h] [package] ...");
2759 pw.println(" -h: print this help");
2760 pw.println(" [package] is an optional package name to limit the output to.");
2761 }
2762
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002763 /** Sort jobs by caller UID, then by Job ID. */
2764 private static void sortJobs(List<JobStatus> jobs) {
2765 Collections.sort(jobs, new Comparator<JobStatus>() {
2766 @Override
2767 public int compare(JobStatus o1, JobStatus o2) {
2768 int uid1 = o1.getUid();
2769 int uid2 = o2.getUid();
2770 int id1 = o1.getJobId();
2771 int id2 = o2.getJobId();
2772 if (uid1 != uid2) {
2773 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002774 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002775 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002776 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002777 });
2778 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002779
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002780 void dumpInternal(final PrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002781 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07002782 final long nowElapsed = sElapsedRealtimeClock.millis();
2783 final long nowUptime = sUptimeMillisClock.millis();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002784 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002785 mConstants.dump(pw);
2786 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07002787 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002788 pw.print("Registered ");
2789 pw.print(mJobs.size());
2790 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07002791 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002792 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002793 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002794 for (JobStatus job : jobs) {
2795 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2796 pw.println(job.toShortStringExceptUniqueId());
2797
2798 // Skip printing details if the caller requested a filter
2799 if (!job.shouldDump(filterUidFinal)) {
2800 continue;
2801 }
2802
Dianne Hackborn6d068262017-05-16 13:14:37 -07002803 job.dump(pw, " ", true, nowElapsed);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002804 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002805 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002806 pw.print(" (job=");
2807 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002808 pw.print(" user=");
2809 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002810 pw.print(" !pending=");
2811 pw.print(!mPendingJobs.contains(job));
2812 pw.print(" !active=");
2813 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002814 pw.print(" !backingup=");
2815 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002816 pw.print(" comp=");
2817 boolean componentPresent = false;
2818 try {
2819 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2820 job.getServiceComponent(),
2821 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2822 job.getUserId()) != null);
2823 } catch (RemoteException e) {
2824 }
2825 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002826 pw.println(")");
2827 }
Christopher Tate7060b042014-06-09 19:50:00 -07002828 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002829 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002830 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002831 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002832 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002833 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07002834 }
2835 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002836 pw.println("Uid priority overrides:");
2837 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002838 int uid = mUidPriorityOverride.keyAt(i);
2839 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2840 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2841 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2842 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002843 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002844 if (mBackingUpUids.size() > 0) {
2845 pw.println();
2846 pw.println("Backing up uids:");
2847 boolean first = true;
2848 for (int i = 0; i < mBackingUpUids.size(); i++) {
2849 int uid = mBackingUpUids.keyAt(i);
2850 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2851 if (first) {
2852 pw.print(" ");
2853 first = false;
2854 } else {
2855 pw.print(", ");
2856 }
2857 pw.print(UserHandle.formatUid(uid));
2858 }
2859 }
2860 pw.println();
2861 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002862 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002863 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002864 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002865 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2866 pw.println();
2867 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002868 pw.println("Pending queue:");
2869 for (int i=0; i<mPendingJobs.size(); i++) {
2870 JobStatus job = mPendingJobs.get(i);
2871 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2872 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07002873 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002874 int priority = evaluateJobPriorityLocked(job);
2875 if (priority != JobInfo.PRIORITY_DEFAULT) {
2876 pw.print(" Evaluated priority: "); pw.println(priority);
2877 }
2878 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07002879 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002880 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002881 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002882 }
Christopher Tate7060b042014-06-09 19:50:00 -07002883 pw.println();
2884 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002885 for (int i=0; i<mActiveServices.size(); i++) {
2886 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002887 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002888 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07002889 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07002890 if (jsc.mStoppedReason != null) {
2891 pw.print("inactive since ");
2892 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
2893 pw.print(", stopped because: ");
2894 pw.println(jsc.mStoppedReason);
2895 } else {
2896 pw.println("inactive");
2897 }
Christopher Tate7060b042014-06-09 19:50:00 -07002898 continue;
2899 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07002900 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08002901 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002902 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002903 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002904 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002905 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002906 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002907 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002908 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002909 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002910 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002911 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002912 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002913 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07002914 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
2915 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07002916 }
2917 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002918 if (filterUid == -1) {
2919 pw.println();
2920 pw.print("mReadyToRock="); pw.println(mReadyToRock);
2921 pw.print("mReportedActive="); pw.println(mReportedActive);
2922 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
2923 }
Makoto Onukie7b02982017-08-24 14:23:36 -07002924 pw.println();
2925 pw.print("PersistStats: ");
2926 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07002927 }
2928 pw.println();
2929 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002930
2931 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
2932 ProtoOutputStream proto = new ProtoOutputStream(fd);
2933 final int filterUidFinal = UserHandle.getAppId(filterUid);
2934 final long nowElapsed = sElapsedRealtimeClock.millis();
2935 final long nowUptime = sUptimeMillisClock.millis();
2936
2937 synchronized (mLock) {
2938 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
2939 for (int u : mStartedUsers) {
2940 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
2941 }
2942 if (mJobs.size() > 0) {
2943 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
2944 sortJobs(jobs);
2945 for (JobStatus job : jobs) {
2946 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
2947 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
2948
2949 // Skip printing details if the caller requested a filter
2950 if (!job.shouldDump(filterUidFinal)) {
2951 continue;
2952 }
2953
2954 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
2955
2956 // isReadyToBeExecuted
2957 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
2958 job.isReady());
2959 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
2960 ArrayUtils.contains(mStartedUsers, job.getUserId()));
2961 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
2962 mPendingJobs.contains(job));
2963 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
2964 isCurrentlyActiveLocked(job));
2965 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
2966 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
2967 boolean componentPresent = false;
2968 try {
2969 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2970 job.getServiceComponent(),
2971 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2972 job.getUserId()) != null);
2973 } catch (RemoteException e) {
2974 }
2975 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
2976 componentPresent);
2977
2978 proto.end(rjToken);
2979 }
2980 }
2981 for (StateController controller : mControllers) {
2982 controller.dumpControllerStateLocked(
2983 proto, JobSchedulerServiceDumpProto.CONTROLLERS, filterUidFinal);
2984 }
2985 for (int i=0; i< mUidPriorityOverride.size(); i++) {
2986 int uid = mUidPriorityOverride.keyAt(i);
2987 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2988 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
2989 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
2990 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
2991 mUidPriorityOverride.valueAt(i));
2992 proto.end(pToken);
2993 }
2994 }
2995 for (int i = 0; i < mBackingUpUids.size(); i++) {
2996 int uid = mBackingUpUids.keyAt(i);
2997 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2998 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
2999 }
3000 }
3001
3002 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3003 filterUidFinal);
3004 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3005 filterUidFinal);
3006
3007 for (JobStatus job : mPendingJobs) {
3008 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3009
3010 job.writeToShortProto(proto, PendingJob.INFO);
3011 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3012 int priority = evaluateJobPriorityLocked(job);
3013 if (priority != JobInfo.PRIORITY_DEFAULT) {
3014 proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3015 }
3016 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3017
3018 proto.end(pjToken);
3019 }
3020 for (JobServiceContext jsc : mActiveServices) {
3021 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3022 final JobStatus job = jsc.getRunningJobLocked();
3023
3024 if (job == null) {
3025 final long ijToken = proto.start(ActiveJob.INACTIVE);
3026
3027 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3028 nowElapsed - jsc.mStoppedTime);
3029 if (jsc.mStoppedReason != null) {
3030 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3031 jsc.mStoppedReason);
3032 }
3033
3034 proto.end(ijToken);
3035 } else {
3036 final long rjToken = proto.start(ActiveJob.RUNNING);
3037
3038 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3039
3040 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3041 nowElapsed - jsc.getExecutionStartTimeElapsed());
3042 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3043 jsc.getTimeoutElapsed() - nowElapsed);
3044
3045 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3046
3047 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3048 if (priority != JobInfo.PRIORITY_DEFAULT) {
3049 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3050 }
3051
3052 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3053 nowUptime - job.madeActive);
3054 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3055 job.madeActive - job.madePending);
3056
3057 proto.end(rjToken);
3058 }
3059 proto.end(ajToken);
3060 }
3061 if (filterUid == -1) {
3062 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3063 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3064 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3065 }
3066 }
3067
3068 proto.flush();
3069 }
Christopher Tate7060b042014-06-09 19:50:00 -07003070}