blob: 740866c9aec33ae1bded7661962acccd427be8d9 [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 Tated1aebb32018-01-31 13:24:14 -080026import android.app.AlarmManager;
Christopher Tate5568f542014-06-18 13:53:31 -070027import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070028import android.app.IUidObserver;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070029import android.app.job.IJobScheduler;
Christopher Tate7060b042014-06-09 19:50:00 -070030import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000031import android.app.job.JobParameters;
Christopher Tate7060b042014-06-09 19:50:00 -070032import android.app.job.JobScheduler;
33import android.app.job.JobService;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070034import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080035import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070036import android.app.usage.UsageStatsManagerInternal;
37import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070038import android.content.BroadcastReceiver;
39import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070040import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070041import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070044import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070045import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060046import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070047import android.content.pm.PackageManagerInternal;
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;
Amith Yamasani977e11f2018-02-16 11:29:54 -080052import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070053import android.os.Binder;
54import android.os.Handler;
55import android.os.Looper;
56import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070057import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070058import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080059import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070060import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070061import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070062import android.os.SystemClock;
63import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070064import android.os.UserManagerInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070065import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080066import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070067import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070068import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070069import android.util.Slog;
70import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080071import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080072import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080073import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080074import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080075
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070076import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070077import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070078import com.android.internal.app.procstats.ProcessStats;
Christopher Tatea732f012017-10-26 17:26:53 -070079import com.android.internal.os.BackgroundThread;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070080import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060081import com.android.internal.util.DumpUtils;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070082import com.android.internal.util.IndentingPrintWriter;
Makoto Onuki15407842018-01-19 14:23:11 -080083import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070084import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080085import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070086import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080087import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080088import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
89import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080090import com.android.server.job.controllers.AppIdleController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070091import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070092import com.android.server.job.controllers.BatteryController;
93import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080094import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070095import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070096import com.android.server.job.controllers.IdleController;
97import com.android.server.job.controllers.JobStatus;
98import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070099import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700100import com.android.server.job.controllers.TimeController;
101
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700102import libcore.util.EmptyArray;
103
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700104import java.io.FileDescriptor;
105import java.io.PrintWriter;
106import java.time.Clock;
107import java.util.ArrayList;
108import java.util.Arrays;
109import java.util.Collections;
110import java.util.Comparator;
111import java.util.Iterator;
112import java.util.List;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700113import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800114import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700115
Christopher Tate7060b042014-06-09 19:50:00 -0700116/**
117 * Responsible for taking jobs representing work to be performed by a client app, and determining
118 * based on the criteria specified when that job should be run against the client application's
119 * endpoint.
120 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
121 * about constraints, or the state of active jobs. It receives callbacks from the various
122 * controllers and completed jobs and operates accordingly.
123 *
124 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
125 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
126 * @hide
127 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800128public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700129 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700130 public static final String TAG = "JobScheduler";
131 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700132 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800133
Dianne Hackborn970510b2016-02-24 16:56:42 -0800134 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700135 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800136 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800137 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800138 /** The maximum number of jobs that we allow an unprivileged app to schedule */
139 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700140
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700141 @VisibleForTesting
142 public static Clock sSystemClock = Clock.systemUTC();
143 @VisibleForTesting
144 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
145 @VisibleForTesting
146 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800147
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800148 /** Global local for all job scheduler state. */
149 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700150 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700151 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700152 /** Tracking the standby bucket state of each app */
153 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700154 /** Tracking amount of time each package runs for. */
155 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700156
157 static final int MSG_JOB_EXPIRED = 0;
158 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700159 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000160 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700161
Christopher Tate7060b042014-06-09 19:50:00 -0700162 /**
163 * Track Services that have currently active or pending jobs. The index is provided by
164 * {@link JobStatus#getServiceToken()}
165 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700166 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700167
Christopher Tate7060b042014-06-09 19:50:00 -0700168 /** List of controllers that will notify this service of updates to jobs. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700169 private final List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800170 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700171 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700172 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700173 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700174 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700175 private final DeviceIdleJobsController mDeviceIdleJobsController;
176
Christopher Tate7060b042014-06-09 19:50:00 -0700177 /**
178 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
179 * when ready to execute them.
180 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700181 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700182
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700183 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700184
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700185 final JobHandler mHandler;
186 final JobSchedulerStub mJobSchedulerStub;
187
Christopher Tatea732f012017-10-26 17:26:53 -0700188 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800189 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700190 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800191 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800192 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800193 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700194
195 /**
196 * Set to true once we are allowed to run third party apps.
197 */
198 boolean mReadyToRock;
199
Christopher Tate7060b042014-06-09 19:50:00 -0700200 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800201 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800202 */
203 boolean mReportedActive;
204
205 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800206 * Are we currently in device-wide standby parole?
207 */
208 volatile boolean mInParole;
209
210 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800211 * Current limit on the number of concurrent JobServiceContext entries we want to
212 * keep actively running a job.
213 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700214 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800215
216 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800217 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800218 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800219 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
220
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700221 /**
222 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
223 */
224 final SparseIntArray mBackingUpUids = new SparseIntArray();
225
Christopher Tatea732f012017-10-26 17:26:53 -0700226 /**
227 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
228 * next become runnable. Index into this array is by normalized bucket:
229 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
230 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
231 * and NEVER apps don't get them at all.
232 */
233 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
234 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800235 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
236
237 static final String HEARTBEAT_TAG = "*job.heartbeat*";
238 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700239
Dianne Hackborn970510b2016-02-24 16:56:42 -0800240 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
241
242 /**
243 * This array essentially stores the state of mActiveServices array.
244 * The ith index stores the job present on the ith JobServiceContext.
245 * We manipulate this array until we arrive at what jobs should be running on
246 * what JobServiceContext.
247 */
248 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
249 /**
250 * Indicates whether we need to act on this jobContext id
251 */
252 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
253 /**
254 * The uid whose jobs we would like to assign to a context.
255 */
256 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800257
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700258 private class ConstantsObserver extends ContentObserver {
259 private ContentResolver mResolver;
260
261 public ConstantsObserver(Handler handler) {
262 super(handler);
263 }
264
265 public void start(ContentResolver resolver) {
266 mResolver = resolver;
267 mResolver.registerContentObserver(Settings.Global.getUriFor(
268 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
269 updateConstants();
270 }
271
272 @Override
273 public void onChange(boolean selfChange, Uri uri) {
274 updateConstants();
275 }
276
277 private void updateConstants() {
278 synchronized (mLock) {
279 try {
280 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
281 Settings.Global.JOB_SCHEDULER_CONSTANTS));
282 } catch (IllegalArgumentException e) {
283 // Failed to parse the settings string, log this and move on
284 // with defaults.
285 Slog.e(TAG, "Bad jobscheduler settings", e);
286 }
287 }
288
289 // Reset the heartbeat alarm based on the new heartbeat duration
290 setNextHeartbeatAlarm();
291 }
292 }
293
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800294 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700295 * All times are in milliseconds. These constants are kept synchronized with the system
296 * global Settings. Any access to this class or its fields should be done while
297 * holding the JobSchedulerService.mLock lock.
298 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700299 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700300 // Key names stored in the settings value.
301 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
302 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800303 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700304 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700305 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
306 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
307 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
308 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
309 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
310 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
311 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
312 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
313 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
314 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700315 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
316 = "max_standard_reschedule_count";
317 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
318 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
319 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700320 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
321 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
322 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
323 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700324 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
325 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700326
327 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
328 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800329 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700330 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700331 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
332 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
333 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
334 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
335 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
336 private static final int DEFAULT_FG_JOB_COUNT = 4;
337 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
338 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700339 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700340 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700341 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
342 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
343 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
344 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700345 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000346 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
347 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700348 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700349 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
350 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700351
352 /**
353 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
354 * early.
355 */
356 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
357 /**
358 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
359 * things early.
360 */
361 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
362 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800363 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
364 * schedule things early.
365 */
366 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
367 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700368 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
369 * schedule things early.
370 */
371 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
372 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700373 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
374 * things early. 1 == Run connectivity jobs as soon as ready.
375 */
376 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
377 /**
378 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
379 * schedule things early.
380 */
381 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
382 /**
383 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
384 * running some work early. This (and thus the other min counts) is now set to 1, to
385 * prevent any batching at this level. Since we now do batching through doze, that is
386 * a much better mechanism.
387 */
388 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
389 /**
390 * This is the job execution factor that is considered to be heavy use of the system.
391 */
392 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
393 /**
394 * This is the job execution factor that is considered to be moderate use of the system.
395 */
396 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
397 /**
398 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
399 */
400 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
401 /**
402 * The maximum number of background jobs we allow when the system is in a normal
403 * memory state.
404 */
405 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
406 /**
407 * The maximum number of background jobs we allow when the system is in a moderate
408 * memory state.
409 */
410 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
411 /**
412 * The maximum number of background jobs we allow when the system is in a low
413 * memory state.
414 */
415 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
416 /**
417 * The maximum number of background jobs we allow when the system is in a critical
418 * memory state.
419 */
420 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700421 /**
422 * The maximum number of times we allow a job to have itself rescheduled before
423 * giving up on it, for standard jobs.
424 */
425 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
426 /**
427 * The maximum number of times we allow a job to have itself rescheduled before
428 * giving up on it, for jobs that are executing work.
429 */
430 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
431 /**
432 * The minimum backoff time to allow for linear backoff.
433 */
434 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
435 /**
436 * The minimum backoff time to allow for exponential backoff.
437 */
438 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700439 /**
440 * How often we recalculate runnability based on apps' standby bucket assignment.
441 * This should be prime relative to common time interval lengths such as a quarter-
442 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
443 */
444 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700445 /**
446 * Mapping: standby bucket -> number of heartbeats between each sweep of that
447 * bucket's jobs.
448 *
449 * Bucket assignments as recorded in the JobStatus objects are normalized to be
450 * indices into this array, rather than the raw constants used
451 * by AppIdleHistory.
452 */
453 final int[] STANDBY_BEATS = {
454 0,
455 DEFAULT_STANDBY_WORKING_BEATS,
456 DEFAULT_STANDBY_FREQUENT_BEATS,
457 DEFAULT_STANDBY_RARE_BEATS
458 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700459 /**
460 * The fraction of a job's running window that must pass before we
461 * consider running it when the network is congested.
462 */
463 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
464 /**
465 * The fraction of a prefetch job's running window that must pass before
466 * we consider matching it against a metered network.
467 */
468 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700469
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700470 private final KeyValueListParser mParser = new KeyValueListParser(',');
471
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700472 void updateConstantsLocked(String value) {
473 try {
474 mParser.setString(value);
475 } catch (Exception e) {
476 // Failed to parse the settings string, log this and move on
477 // with defaults.
478 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700479 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800480
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700481 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
482 DEFAULT_MIN_IDLE_COUNT);
483 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
484 DEFAULT_MIN_CHARGING_COUNT);
485 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
486 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
487 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
488 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
489 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
490 DEFAULT_MIN_CONNECTIVITY_COUNT);
491 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
492 DEFAULT_MIN_CONTENT_COUNT);
493 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
494 DEFAULT_MIN_READY_JOBS_COUNT);
495 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
496 DEFAULT_HEAVY_USE_FACTOR);
497 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
498 DEFAULT_MODERATE_USE_FACTOR);
499 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
500 DEFAULT_FG_JOB_COUNT);
501 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
502 DEFAULT_BG_NORMAL_JOB_COUNT);
503 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
504 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
505 }
506 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
507 DEFAULT_BG_MODERATE_JOB_COUNT);
508 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
509 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
510 }
511 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
512 DEFAULT_BG_LOW_JOB_COUNT);
513 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
514 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
515 }
516 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
517 DEFAULT_BG_CRITICAL_JOB_COUNT);
518 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
519 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
520 }
521 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
522 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
523 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
524 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
525 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
526 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
527 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
528 DEFAULT_MIN_EXP_BACKOFF_TIME);
529 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
530 DEFAULT_STANDBY_HEARTBEAT_TIME);
531 STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
532 DEFAULT_STANDBY_WORKING_BEATS);
533 STANDBY_BEATS[2] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
534 DEFAULT_STANDBY_FREQUENT_BEATS);
535 STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
536 DEFAULT_STANDBY_RARE_BEATS);
537 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
538 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
539 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
540 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700541 }
542
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700543 void dump(IndentingPrintWriter pw) {
544 pw.println("Settings:");
545 pw.increaseIndent();
546 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
547 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
548 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
549 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
550 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
551 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
552 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
553 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
554 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
555 pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
556 pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
557 pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
558 pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
559 pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
560 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
561 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
562 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
563 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
564 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
565 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700566 pw.print(STANDBY_BEATS[0]);
567 for (int i = 1; i < STANDBY_BEATS.length; i++) {
568 pw.print(", ");
569 pw.print(STANDBY_BEATS[i]);
570 }
571 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700572 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
573 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
574 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700575 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800576
577 void dump(ProtoOutputStream proto, long fieldId) {
578 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800579 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
580 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
581 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
582 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
583 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
584 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
585 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
586 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
587 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
588 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
589 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
590 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
591 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
592 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
593 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
594 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
595 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
596 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
597 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800598 for (int period : STANDBY_BEATS) {
599 proto.write(ConstantsProto.STANDBY_BEATS, period);
600 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700601 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
602 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800603 proto.end(token);
604 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700605 }
606
607 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700608 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700609
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700610 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
611 if (o1.enqueueTime < o2.enqueueTime) {
612 return -1;
613 }
614 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
615 };
616
617 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
618 int where = Collections.binarySearch(array, newItem, comparator);
619 if (where < 0) {
620 where = ~where;
621 }
622 array.add(where, newItem);
623 }
624
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700625 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700626 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
627 * still clean up. On reinstall the package will have a new uid.
628 */
629 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
630 @Override
631 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700632 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700633 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700634 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700635 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700636 final String pkgName = getPackageName(intent);
637 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
638
Christopher Tateee7805b2016-07-15 16:56:56 -0700639 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700640 // Purge the app's jobs if the whole package was just disabled. When this is
641 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700642 if (pkgName != null && pkgUid != -1) {
643 final String[] changedComponents = intent.getStringArrayExtra(
644 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
645 if (changedComponents != null) {
646 for (String component : changedComponents) {
647 if (component.equals(pkgName)) {
648 if (DEBUG) {
649 Slog.d(TAG, "Package state change: " + pkgName);
650 }
651 try {
652 final int userId = UserHandle.getUserId(pkgUid);
653 IPackageManager pm = AppGlobals.getPackageManager();
654 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
655 if (state == COMPONENT_ENABLED_STATE_DISABLED
656 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
657 if (DEBUG) {
658 Slog.d(TAG, "Removing jobs for package " + pkgName
659 + " in user " + userId);
660 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700661 cancelJobsForPackageAndUid(pkgName, pkgUid,
662 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700663 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700664 } catch (RemoteException|IllegalArgumentException e) {
665 /*
666 * IllegalArgumentException means that the package doesn't exist.
667 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
668 * behind outright uninstall, so by the time we try to act it's gone.
669 * We don't need to act on this PACKAGE_CHANGED when this happens;
670 * we'll get a PACKAGE_REMOVED later and clean up then.
671 *
672 * RemoteException can't actually happen; the package manager is
673 * running in this same process.
674 */
675 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700676 break;
677 }
678 }
679 }
680 } else {
681 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
682 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700683 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700684 // If this is an outright uninstall rather than the first half of an
685 // app update sequence, cancel the jobs associated with the app.
686 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
687 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
688 if (DEBUG) {
689 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
690 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700691 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Christopher Tate7060b042014-06-09 19:50:00 -0700692 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700693 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700694 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
695 if (DEBUG) {
696 Slog.d(TAG, "Removing jobs for user: " + userId);
697 }
698 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700699 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
700 // Has this package scheduled any jobs, such that we will take action
701 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700702 if (pkgUid != -1) {
703 List<JobStatus> jobsForUid;
704 synchronized (mLock) {
705 jobsForUid = mJobs.getJobsByUid(pkgUid);
706 }
707 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
708 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
709 if (DEBUG) {
710 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
711 + pkgUid + " has jobs");
712 }
713 setResultCode(Activity.RESULT_OK);
714 break;
715 }
716 }
717 }
718 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
719 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700720 if (pkgUid != -1) {
721 if (DEBUG) {
722 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
723 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700724 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700725 }
Christopher Tate7060b042014-06-09 19:50:00 -0700726 }
727 }
728 };
729
Christopher Tateb5c07882016-05-26 17:11:09 -0700730 private String getPackageName(Intent intent) {
731 Uri uri = intent.getData();
732 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
733 return pkg;
734 }
735
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700736 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700737 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800738 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700739 }
740
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700741 @Override public void onUidGone(int uid, boolean disabled) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800742 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800743 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700744 cancelJobsForUid(uid, "uid gone");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800745 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700746 synchronized (mLock) {
747 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
748 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700749 }
750
751 @Override public void onUidActive(int uid) throws RemoteException {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700752 synchronized (mLock) {
753 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
754 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700755 }
756
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700757 @Override public void onUidIdle(int uid, boolean disabled) {
Dianne Hackborne07641d2016-11-09 15:07:23 -0800758 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700759 cancelJobsForUid(uid, "app uid idle");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800760 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700761 synchronized (mLock) {
762 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
763 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700764 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700765
766 @Override public void onUidCachedChanged(int uid, boolean cached) {
767 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700768 };
769
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800770 public Object getLock() {
771 return mLock;
772 }
773
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700774 public JobStore getJobStore() {
775 return mJobs;
776 }
777
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700778 public Constants getConstants() {
779 return mConstants;
780 }
781
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700782 @Override
783 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700784 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
785 // Let's kick any outstanding jobs for this user.
786 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
787 }
788
789 @Override
790 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700791 // Let's kick any outstanding jobs for this user.
792 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
793 }
794
795 @Override
796 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700797 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700798 }
799
Makoto Onuki15407842018-01-19 14:23:11 -0800800 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800801 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800802 */
Makoto Onukie4918212018-02-06 11:30:15 -0800803 private boolean isUidActive(int uid) {
804 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800805 }
806
Makoto Onukie4918212018-02-06 11:30:15 -0800807 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800808
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700809 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
810 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700811 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800812 if (ActivityManager.getService().isAppStartModeDisabled(uId,
813 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700814 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
815 + " -- package not allowed to start");
816 return JobScheduler.RESULT_FAILURE;
817 }
818 } catch (RemoteException e) {
819 }
Christopher Tatea732f012017-10-26 17:26:53 -0700820
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800821 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700822 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
823
824 if (work != null && toCancel != null) {
825 // Fast path: we are adding work to an existing job, and the JobInfo is not
826 // changing. We can just directly enqueue this work in to the job.
827 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -0800828
Dianne Hackborn342e6032017-04-13 18:04:31 -0700829 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -0800830
831 // If any of work item is enqueued when the source is in the foreground,
832 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -0800833 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800834
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700835 return JobScheduler.RESULT_SUCCESS;
836 }
837 }
838
839 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -0800840
841 // Give exemption if the source is in the foreground just now.
842 // Note if it's a sync job, this method is called on the handler so it's not exactly
843 // the state when requestSync() was called, but that should be fine because of the
844 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -0800845 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800846
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700847 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800848 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800849 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800850 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
851 Slog.w(TAG, "Too many jobs for uid " + uId);
852 throw new IllegalStateException("Apps may not schedule more than "
853 + MAX_JOBS_PER_APP + " distinct jobs");
854 }
855 }
856
Dianne Hackborna47223f2017-03-30 13:49:13 -0700857 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700858 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700859
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700860 if (toCancel != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700861 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700862 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700863 if (work != null) {
864 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700865 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700866 }
867 startTrackingJobLocked(jobStatus, toCancel);
Tej Singhd5747a62018-01-08 20:57:35 -0800868 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -0800869 uId, null, jobStatus.getBatteryName(),
870 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700871
872 // If the job is immediately ready to run, then we can just immediately
873 // put it in the pending list and try to schedule it. This is especially
874 // important for jobs with a 0 deadline constraint, since they will happen a fair
875 // amount, we want to handle them as quickly as possible, and semantically we want to
876 // make sure we have started holding the wake lock for the job before returning to
877 // the caller.
878 // If the job is not yet ready to run, there is nothing more to do -- we are
879 // now just waiting for one of its controllers to change state and schedule
880 // the job appropriately.
881 if (isReadyToBeExecutedLocked(jobStatus)) {
882 // This is a new job, we can just immediately put it on the pending
883 // list and try to run it.
884 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700885 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700886 maybeRunPendingJobsLocked();
887 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800888 }
Christopher Tate7060b042014-06-09 19:50:00 -0700889 return JobScheduler.RESULT_SUCCESS;
890 }
891
892 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800893 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800894 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
895 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
896 for (int i = jobs.size() - 1; i >= 0; i--) {
897 JobStatus job = jobs.get(i);
898 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700899 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800900 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700901 }
Christopher Tate7060b042014-06-09 19:50:00 -0700902 }
903
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600904 public JobInfo getPendingJob(int uid, int jobId) {
905 synchronized (mLock) {
906 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
907 for (int i = jobs.size() - 1; i >= 0; i--) {
908 JobStatus job = jobs.get(i);
909 if (job.getJobId() == jobId) {
910 return job.getJob();
911 }
912 }
913 return null;
914 }
915 }
916
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700917 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800918 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700919 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
920 for (int i=0; i<jobsForUser.size(); i++) {
921 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700922 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700923 }
Christopher Tate7060b042014-06-09 19:50:00 -0700924 }
925 }
926
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -0700927 private void cancelJobsForNonExistentUsers() {
928 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
929 synchronized (mLock) {
930 mJobs.removeJobsOfNonUsers(umi.getUserIds());
931 }
932 }
933
Makoto Onukie7b96182017-08-30 14:53:16 -0700934 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
935 if ("android".equals(pkgName)) {
936 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
937 return;
938 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700939 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700940 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
941 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
942 final JobStatus job = jobsForUid.get(i);
943 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700944 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700945 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700946 }
947 }
948 }
949
Christopher Tate7060b042014-06-09 19:50:00 -0700950 /**
951 * Entry point from client to cancel all jobs originating from their uid.
952 * This will remove the job from the master list, and cancel the job if it was staged for
953 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700954 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800955 *
Christopher Tate7060b042014-06-09 19:50:00 -0700956 */
Christopher Tate8c67d122017-09-29 16:54:26 -0700957 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -0700958 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700959 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -0700960 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -0700961 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700962
963 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800964 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700965 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
966 for (int i=0; i<jobsForUid.size(); i++) {
967 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700968 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -0700969 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700970 }
Christopher Tate7060b042014-06-09 19:50:00 -0700971 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700972 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -0700973 }
974
975 /**
976 * Entry point from client to cancel the job corresponding to the jobId provided.
977 * This will remove the job from the master list, and cancel the job if it was staged for
978 * execution or being executed.
979 * @param uid Uid of the calling client.
980 * @param jobId Id of the job, provided at schedule-time.
981 */
Makoto Onukid2bfec62018-01-12 13:58:01 -0800982 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -0700983 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800984 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700985 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700986 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -0800987 cancelJobImplLocked(toCancel, null,
988 "cancel() called by app, callingUid=" + callingUid
989 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700990 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700991 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -0700992 }
993 }
994
Dianne Hackborn729a3282017-06-09 16:06:01 -0700995 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700996 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
997 cancelled.unprepareLocked(ActivityManager.getService());
998 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
999 // Remove from pending queue.
1000 if (mPendingJobs.remove(cancelled)) {
1001 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001002 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001003 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001004 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001005 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001006 }
1007
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001008 void updateUidState(int uid, int procState) {
1009 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001010 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1011 // Only use this if we are exactly the top app. All others can live
1012 // with just the foreground priority. This means that persistent processes
1013 // can never be the top app priority... that is fine.
1014 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001015 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001016 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001017 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001018 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001019 }
1020 }
1021 }
1022
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001023 @Override
1024 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001025 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001026 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001027 // When becoming idle, make sure no jobs are actively running,
1028 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001029 for (int i=0; i<mActiveServices.size(); i++) {
1030 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001031 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001032 if (executing != null
1033 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001034 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1035 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001036 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001037 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001038 } else {
1039 // When coming out of idle, allow thing to start back up.
1040 if (mReadyToRock) {
1041 if (mLocalDeviceIdleController != null) {
1042 if (!mReportedActive) {
1043 mReportedActive = true;
1044 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001045 }
1046 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001047 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001048 }
1049 }
1050 }
1051 }
1052
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001053 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001054 // active is true if pending queue contains jobs OR some job is running.
1055 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001056 if (mPendingJobs.size() <= 0) {
1057 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001058 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001059 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001060 if (job != null
1061 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
1062 && !job.dozeWhitelisted) {
1063 // We will report active if we have a job running and it is not an exception
1064 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001065 active = true;
1066 break;
1067 }
1068 }
1069 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001070
1071 if (mReportedActive != active) {
1072 mReportedActive = active;
1073 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001074 mLocalDeviceIdleController.setJobsActive(active);
1075 }
1076 }
1077 }
1078
Christopher Tated117b292018-01-05 17:32:36 -08001079 void reportAppUsage(String packageName, int userId) {
1080 // This app just transitioned into interactive use or near equivalent, so we should
1081 // take a look at its job state for feedback purposes.
1082 }
1083
Christopher Tate7060b042014-06-09 19:50:00 -07001084 /**
1085 * Initializes the system service.
1086 * <p>
1087 * Subclasses must define a single argument constructor that accepts the context
1088 * and passes it to super.
1089 * </p>
1090 *
1091 * @param context The system server context.
1092 */
1093 public JobSchedulerService(Context context) {
1094 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001095
1096 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001097 mActivityManagerInternal = Preconditions.checkNotNull(
1098 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001099
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001100 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001101 mConstants = new Constants();
1102 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001103 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001104
1105 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001106 mStandbyTracker = new StandbyTracker();
1107 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1108 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001109
1110 // The job store needs to call back
1111 publishLocalService(JobSchedulerInternal.class, new LocalService());
1112
1113 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001114 mJobs = JobStore.initAndGet(this);
1115
Christopher Tate7060b042014-06-09 19:50:00 -07001116 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001117 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001118 mControllers.add(new ConnectivityController(this));
1119 mControllers.add(new TimeController(this));
1120 mControllers.add(new IdleController(this));
1121 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001122 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001123 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001124 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001125 mControllers.add(new BackgroundJobsController(this));
1126 mControllers.add(new AppIdleController(this));
1127 mControllers.add(new ContentObserverController(this));
1128 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001129 mControllers.add(mDeviceIdleJobsController);
Christopher Tate616541d2017-07-26 14:27:38 -07001130
1131 // If the job store determined that it can't yet reschedule persisted jobs,
1132 // we need to start watching the clock.
1133 if (!mJobs.jobTimesInflatedValid()) {
1134 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1135 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1136 }
Christopher Tate7060b042014-06-09 19:50:00 -07001137 }
1138
Christopher Tate616541d2017-07-26 14:27:38 -07001139 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1140 @Override
1141 public void onReceive(Context context, Intent intent) {
1142 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1143 // When we reach clock sanity, recalculate the temporal windows
1144 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001145 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001146 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1147
1148 // We've done our job now, so stop watching the time.
1149 context.unregisterReceiver(this);
1150
1151 // And kick off the work to update the affected jobs, using a secondary
1152 // thread instead of chugging away here on the main looper thread.
1153 FgThread.getHandler().post(mJobTimeUpdater);
1154 }
1155 }
1156 }
1157 };
1158
1159 private final Runnable mJobTimeUpdater = () -> {
1160 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1161 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1162 synchronized (mLock) {
1163 // Note: we intentionally both look up the existing affected jobs and replace them
1164 // with recalculated ones inside the same lock lifetime.
1165 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1166
1167 // Now, at each position [i], we have both the existing JobStatus
1168 // and the one that replaces it.
1169 final int N = toAdd.size();
1170 for (int i = 0; i < N; i++) {
1171 final JobStatus oldJob = toRemove.get(i);
1172 final JobStatus newJob = toAdd.get(i);
1173 if (DEBUG) {
1174 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1175 }
1176 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1177 }
1178 }
1179 };
1180
Christopher Tate7060b042014-06-09 19:50:00 -07001181 @Override
1182 public void onStart() {
1183 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1184 }
1185
1186 @Override
1187 public void onBootPhase(int phase) {
1188 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001189 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001190
Makoto Onukie4918212018-02-06 11:30:15 -08001191 mAppStateTracker = Preconditions.checkNotNull(
1192 LocalServices.getService(AppStateTracker.class));
Christopher Tated1aebb32018-01-31 13:24:14 -08001193 setNextHeartbeatAlarm();
Makoto Onuki15407842018-01-19 14:23:11 -08001194
Shreyas Basarge5db09082016-01-07 13:38:29 +00001195 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001196 final IntentFilter filter = new IntentFilter();
1197 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1198 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001199 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1200 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001201 filter.addDataScheme("package");
1202 getContext().registerReceiverAsUser(
1203 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1204 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1205 getContext().registerReceiverAsUser(
1206 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001207 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001208 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001209 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001210 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1211 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001212 } catch (RemoteException e) {
1213 // ignored; both services live in system_server
1214 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001215 // Remove any jobs that are not associated with any of the current users.
1216 cancelJobsForNonExistentUsers();
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001217 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001218 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001219 // Let's go!
1220 mReadyToRock = true;
1221 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1222 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001223 mLocalDeviceIdleController
1224 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001225 // Create the "runners".
1226 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1227 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001228 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001229 getContext().getMainLooper()));
1230 }
1231 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001232 mJobs.forEachJob((job) -> {
1233 for (int controller = 0; controller < mControllers.size(); controller++) {
1234 final StateController sc = mControllers.get(controller);
1235 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001236 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001237 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001238 // GO GO GO!
1239 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1240 }
Christopher Tate7060b042014-06-09 19:50:00 -07001241 }
1242 }
1243
1244 /**
1245 * Called when we have a job status object that we need to insert in our
1246 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1247 * about.
1248 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001249 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1250 if (!jobStatus.isPreparedLocked()) {
1251 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1252 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001253 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001254 final boolean update = mJobs.add(jobStatus);
1255 if (mReadyToRock) {
1256 for (int i = 0; i < mControllers.size(); i++) {
1257 StateController controller = mControllers.get(i);
1258 if (update) {
1259 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001260 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001261 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001262 }
Christopher Tate7060b042014-06-09 19:50:00 -07001263 }
1264 }
1265
1266 /**
1267 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1268 * object removed.
1269 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001270 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001271 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001272 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001273 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001274
1275 // Remove from store as well as controllers.
1276 final boolean removed = mJobs.remove(jobStatus, writeBack);
1277 if (removed && mReadyToRock) {
1278 for (int i=0; i<mControllers.size(); i++) {
1279 StateController controller = mControllers.get(i);
1280 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001281 }
1282 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001283 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001284 }
1285
Dianne Hackborn729a3282017-06-09 16:06:01 -07001286 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001287 for (int i=0; i<mActiveServices.size(); i++) {
1288 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001289 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001290 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001291 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001292 return true;
1293 }
1294 }
1295 return false;
1296 }
1297
1298 /**
1299 * @param job JobStatus we are querying against.
1300 * @return Whether or not the job represented by the status object is currently being run or
1301 * is pending.
1302 */
1303 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001304 for (int i=0; i<mActiveServices.size(); i++) {
1305 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001306 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001307 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1308 return true;
1309 }
1310 }
1311 return false;
1312 }
1313
Dianne Hackborn807de782016-04-07 17:54:41 -07001314 void noteJobsPending(List<JobStatus> jobs) {
1315 for (int i = jobs.size() - 1; i >= 0; i--) {
1316 JobStatus job = jobs.get(i);
1317 mJobPackageTracker.notePending(job);
1318 }
1319 }
1320
1321 void noteJobsNonpending(List<JobStatus> jobs) {
1322 for (int i = jobs.size() - 1; i >= 0; i--) {
1323 JobStatus job = jobs.get(i);
1324 mJobPackageTracker.noteNonpending(job);
1325 }
1326 }
1327
Christopher Tate7060b042014-06-09 19:50:00 -07001328 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001329 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1330 * specify an override deadline on a failed job (the failed job will run even though it's not
1331 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1332 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1333 *
Christopher Tate7060b042014-06-09 19:50:00 -07001334 * @param failureToReschedule Provided job status that we will reschedule.
1335 * @return A newly instantiated JobStatus with the same constraints as the last job except
1336 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001337 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001338 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001339 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001340 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001341 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001342 final JobInfo job = failureToReschedule.getJob();
1343
1344 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001345 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1346 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001347
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001348 if (failureToReschedule.hasWorkLocked()) {
1349 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1350 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1351 + backoffAttempts + " > work limit "
1352 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1353 return null;
1354 }
1355 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1356 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1357 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1358 return null;
1359 }
1360
Christopher Tate7060b042014-06-09 19:50:00 -07001361 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001362 case JobInfo.BACKOFF_POLICY_LINEAR: {
1363 long backoff = initialBackoffMillis;
1364 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1365 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1366 }
1367 delayMillis = backoff * backoffAttempts;
1368 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001369 default:
1370 if (DEBUG) {
1371 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1372 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001373 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1374 long backoff = initialBackoffMillis;
1375 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1376 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1377 }
1378 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1379 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001380 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001381 delayMillis =
1382 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001383 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1384 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001385 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001386 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001387 for (int ic=0; ic<mControllers.size(); ic++) {
1388 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001389 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001390 }
1391 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001392 }
1393
1394 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001395 * Called after a periodic has executed so we can reschedule it. We take the last execution
1396 * time of the job to be the time of completion (i.e. the time at which this function is
1397 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001398 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001399 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1400 * to underscheduling at least, rather than if we had taken the last execution time to be the
1401 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001402 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1403 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001404 * @return A new job representing the execution criteria for this instantiation of the
1405 * recurring job.
1406 */
1407 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001408 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001409 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001410 long runEarly = 0L;
1411
1412 // If this periodic was rescheduled it won't have a deadline.
1413 if (periodicToReschedule.hasDeadlineConstraint()) {
1414 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1415 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001416 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001417 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001418 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1419 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001420
1421 if (DEBUG) {
1422 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1423 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1424 }
Christopher Tatea732f012017-10-26 17:26:53 -07001425 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1426 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1427 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001428 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001429 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001430 }
1431
Christopher Tated1aebb32018-01-31 13:24:14 -08001432 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
1433 final long heartbeat;
1434 final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1435 synchronized (mLock) {
1436 heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME);
1437 }
1438 if (DEBUG_STANDBY) {
1439 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId
1440 + " delta=" + timeSinceLastJob);
1441 }
1442 return heartbeat;
1443 }
1444
1445 long heartbeatWhenJobsLastRun(JobStatus job) {
1446 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1447 }
1448
Christopher Tate7060b042014-06-09 19:50:00 -07001449 // JobCompletedListener implementations.
1450
1451 /**
1452 * A job just finished executing. We fetch the
1453 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1454 * whether we want to reschedule we readd it to the controllers.
1455 * @param jobStatus Completed job.
1456 * @param needsReschedule Whether the implementing class should reschedule this job.
1457 */
1458 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001459 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001460 if (DEBUG) {
1461 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1462 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001463
1464 // If the job wants to be rescheduled, we first need to make the next upcoming
1465 // job so we can transfer any appropriate state over from the previous job when
1466 // we stop it.
1467 final JobStatus rescheduledJob = needsReschedule
1468 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1469
Shreyas Basarge73f10252016-02-11 17:06:13 +00001470 // Do not write back immediately if this is a periodic job. The job may get lost if system
1471 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001472 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001473 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001474 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001475 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001476 // We still want to check for jobs to execute, because this job may have
1477 // scheduled a new job under the same job id, and now we can run it.
1478 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001479 return;
1480 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001481
1482 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001483 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001484 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001485 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001486 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001487 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001488 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001489 } else if (jobStatus.getJob().isPeriodic()) {
1490 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001491 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001492 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001493 } catch (SecurityException e) {
1494 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1495 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001496 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001497 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001498 jobStatus.unprepareLocked(ActivityManager.getService());
1499 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001500 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001501 }
1502
1503 // StateChangedListener implementations.
1504
1505 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001506 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1507 * some controller's state has changed, so as to run through the list of jobs and start/stop
1508 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001509 */
1510 @Override
1511 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001512 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001513 }
1514
1515 @Override
1516 public void onRunJobNow(JobStatus jobStatus) {
1517 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1518 }
1519
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001520 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001521
1522 public JobHandler(Looper looper) {
1523 super(looper);
1524 }
1525
1526 @Override
1527 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001528 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001529 if (!mReadyToRock) {
1530 return;
1531 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001532 switch (message.what) {
1533 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001534 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001535 // runNow can be null, which is a controller's way of indicating that its
1536 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001537 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001538 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001539 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001540 } else {
1541 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001542 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001543 } break;
1544 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001545 if (mReportedActive) {
1546 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001547 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001548 } else {
1549 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001550 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001551 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001552 break;
1553 case MSG_CHECK_JOB_GREEDY:
1554 queueReadyJobsForExecutionLocked();
1555 break;
1556 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001557 cancelJobImplLocked((JobStatus) message.obj, null,
1558 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001559 break;
Matthew Williams75fc5252014-09-02 16:17:53 -07001560 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001561 maybeRunPendingJobsLocked();
1562 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1563 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001564 }
1565 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001566 }
Christopher Tate7060b042014-06-09 19:50:00 -07001567
Dianne Hackborn6d068262017-05-16 13:14:37 -07001568 private void stopNonReadyActiveJobsLocked() {
1569 for (int i=0; i<mActiveServices.size(); i++) {
1570 JobServiceContext serviceContext = mActiveServices.get(i);
1571 final JobStatus running = serviceContext.getRunningJobLocked();
1572 if (running != null && !running.isReady()) {
1573 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001574 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1575 "cancelled due to unsatisfied constraints");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001576 }
1577 }
1578 }
1579
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001580 /**
1581 * Run through list of jobs and execute all possible - at least one is expired so we do
1582 * as many as we can.
1583 */
1584 private void queueReadyJobsForExecutionLocked() {
1585 if (DEBUG) {
1586 Slog.d(TAG, "queuing all ready jobs for execution:");
1587 }
1588 noteJobsNonpending(mPendingJobs);
1589 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001590 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001591 mJobs.forEachJob(mReadyQueueFunctor);
1592 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001593
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001594 if (DEBUG) {
1595 final int queuedJobs = mPendingJobs.size();
1596 if (queuedJobs == 0) {
1597 Slog.d(TAG, "No jobs pending.");
1598 } else {
1599 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001600 }
1601 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001602 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001603
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001604 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001605 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001606
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001607 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001608 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001609 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001610 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001611 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001612 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001613 if (newReadyJobs == null) {
1614 newReadyJobs = new ArrayList<JobStatus>();
1615 }
1616 newReadyJobs.add(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001617 }
1618 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001619
1620 public void postProcess() {
1621 if (newReadyJobs != null) {
1622 noteJobsPending(newReadyJobs);
1623 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001624 if (mPendingJobs.size() > 1) {
1625 mPendingJobs.sort(mEnqueueTimeComparator);
1626 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001627 }
1628 newReadyJobs = null;
1629 }
1630 }
1631 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1632
1633 /**
1634 * The state of at least one job has changed. Here is where we could enforce various
1635 * policies on when we want to execute jobs.
1636 * Right now the policy is such:
1637 * If >1 of the ready jobs is idle mode we send all of them off
1638 * if more than 2 network connectivity jobs are ready we send them all off.
1639 * If more than 4 jobs total are ready we send them all off.
1640 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1641 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001642 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001643 int chargingCount;
1644 int batteryNotLowCount;
1645 int storageNotLowCount;
1646 int idleCount;
1647 int backoffCount;
1648 int connectivityCount;
1649 int contentCount;
1650 List<JobStatus> runnableJobs;
1651
1652 public MaybeReadyJobQueueFunctor() {
1653 reset();
1654 }
1655
1656 // Functor method invoked for each job via JobStore.forEachJob()
1657 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001658 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001659 if (isReadyToBeExecutedLocked(job)) {
1660 try {
1661 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1662 job.getJob().getService().getPackageName())) {
1663 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1664 + job.getJob().toString() + " -- package not allowed to start");
1665 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1666 return;
1667 }
1668 } catch (RemoteException e) {
1669 }
1670 if (job.getNumFailures() > 0) {
1671 backoffCount++;
1672 }
1673 if (job.hasIdleConstraint()) {
1674 idleCount++;
1675 }
1676 if (job.hasConnectivityConstraint()) {
1677 connectivityCount++;
1678 }
1679 if (job.hasChargingConstraint()) {
1680 chargingCount++;
1681 }
1682 if (job.hasBatteryNotLowConstraint()) {
1683 batteryNotLowCount++;
1684 }
1685 if (job.hasStorageNotLowConstraint()) {
1686 storageNotLowCount++;
1687 }
1688 if (job.hasContentTriggerConstraint()) {
1689 contentCount++;
1690 }
1691 if (runnableJobs == null) {
1692 runnableJobs = new ArrayList<>();
1693 }
1694 runnableJobs.add(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001695 }
1696 }
1697
1698 public void postProcess() {
1699 if (backoffCount > 0 ||
1700 idleCount >= mConstants.MIN_IDLE_COUNT ||
1701 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1702 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1703 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1704 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1705 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1706 (runnableJobs != null
1707 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1708 if (DEBUG) {
1709 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1710 }
1711 noteJobsPending(runnableJobs);
1712 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001713 if (mPendingJobs.size() > 1) {
1714 mPendingJobs.sort(mEnqueueTimeComparator);
1715 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001716 } else {
1717 if (DEBUG) {
1718 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1719 }
1720 }
1721
1722 // Be ready for next time
1723 reset();
1724 }
1725
1726 private void reset() {
1727 chargingCount = 0;
1728 idleCount = 0;
1729 backoffCount = 0;
1730 connectivityCount = 0;
1731 batteryNotLowCount = 0;
1732 storageNotLowCount = 0;
1733 contentCount = 0;
1734 runnableJobs = null;
1735 }
1736 }
1737 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1738
1739 private void maybeQueueReadyJobsForExecutionLocked() {
1740 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1741
1742 noteJobsNonpending(mPendingJobs);
1743 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001744 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001745 mJobs.forEachJob(mMaybeQueueFunctor);
1746 mMaybeQueueFunctor.postProcess();
1747 }
1748
Christopher Tated1aebb32018-01-31 13:24:14 -08001749 /**
1750 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
1751 */
1752 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1753
1754 @Override
1755 public void onAlarm() {
1756 synchronized (mLock) {
1757 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1758 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1759 if (beatsElapsed > 0) {
1760 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1761 advanceHeartbeatLocked(beatsElapsed);
1762 }
Christopher Tatea732f012017-10-26 17:26:53 -07001763 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001764 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07001765 }
Christopher Tatea732f012017-10-26 17:26:53 -07001766 }
1767
Christopher Tated1aebb32018-01-31 13:24:14 -08001768 // Intentionally does not touch the alarm timing
1769 void advanceHeartbeatLocked(long beatsElapsed) {
1770 mHeartbeat += beatsElapsed;
1771 if (DEBUG_STANDBY) {
1772 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1773 + " to " + mHeartbeat);
1774 }
1775 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
1776 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1777 // new jobs scheduled by apps in that bucket will be permitted to run
1778 // immediately.
1779 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07001780 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001781 // Did we reach or cross a bucket boundary?
1782 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1783 didAdvanceBucket = true;
1784 }
1785 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07001786 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1787 }
1788 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001789 Slog.v(TAG, " Bucket " + i + " next heartbeat "
1790 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07001791 }
1792 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001793
1794 if (didAdvanceBucket) {
1795 if (DEBUG_STANDBY) {
1796 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1797 }
1798 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1799 }
1800 }
1801
1802 void setNextHeartbeatAlarm() {
1803 final long heartbeatLength;
1804 synchronized (mLock) {
1805 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1806 }
1807 final long now = sElapsedRealtimeClock.millis();
1808 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1809 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1810 if (DEBUG_STANDBY) {
1811 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1812 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1813 }
1814 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1815 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1816 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07001817 }
1818
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001819 /**
1820 * Criteria for moving a job into the pending queue:
1821 * - It's ready.
1822 * - It's not pending.
1823 * - It's not already running on a JSC.
1824 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07001825 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001826 * - The component is enabled and runnable.
1827 */
1828 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001829 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001830
1831 if (DEBUG) {
1832 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1833 + " ready=" + jobReady);
1834 }
1835
1836 // This is a condition that is very likely to be false (most jobs that are
1837 // scheduled are sitting there, not ready yet) and very cheap to check (just
1838 // a few conditions on data in JobStatus).
1839 if (!jobReady) {
1840 return false;
1841 }
1842
1843 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001844
1845 final int userId = job.getUserId();
1846 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1847
1848 if (DEBUG) {
1849 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001850 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001851 }
1852
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001853 // These are also fairly cheap to check, though they typically will not
1854 // be conditions we fail.
1855 if (!jobExists || !userStarted) {
1856 return false;
1857 }
1858
1859 final boolean jobPending = mPendingJobs.contains(job);
1860 final boolean jobActive = isCurrentlyActiveLocked(job);
1861
1862 if (DEBUG) {
1863 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1864 + " pending=" + jobPending + " active=" + jobActive);
1865 }
1866
1867 // These can be a little more expensive (especially jobActive, since we need to
1868 // go through the array of all potentially active jobs), so we are doing them
1869 // later... but still before checking with the package manager!
1870 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001871 return false;
1872 }
1873
Christopher Tatea732f012017-10-26 17:26:53 -07001874 // If the app is in a non-active standby bucket, make sure we've waited
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001875 // an appropriate amount of time since the last invocation. During device-
1876 // wide parole, standby bucketing is ignored.
Makoto Onuki959acb52018-01-26 14:10:03 -08001877 //
1878 // But if a job has FLAG_EXEMPT_FROM_APP_STANDBY, don't check it.
1879 if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001880 final int bucket = job.getStandbyBucket();
1881 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001882 // Only skip this job if the app is still waiting for the end of its nominal
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001883 // bucket interval. Once it's waited that long, we let it go ahead and clear.
1884 // The final (NEVER) bucket is special; we never age those apps' jobs into
1885 // runnability.
Christopher Tated1aebb32018-01-31 13:24:14 -08001886 final long appLastRan = heartbeatWhenJobsLastRun(job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001887 if (bucket >= mConstants.STANDBY_BEATS.length
Christopher Tated1aebb32018-01-31 13:24:14 -08001888 || (mHeartbeat > appLastRan
1889 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001890 // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1891 if (job.getWhenStandbyDeferred() == 0) {
1892 if (DEBUG_STANDBY) {
1893 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
Christopher Tated1aebb32018-01-31 13:24:14 -08001894 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
1895 + " for " + job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001896 }
1897 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tate0c4d7682017-12-06 15:10:22 -08001898 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001899 return false;
1900 } else {
1901 if (DEBUG_STANDBY) {
1902 Slog.v(TAG, "Bucket deferred job aged into runnability at "
1903 + mHeartbeat + " : " + job);
1904 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08001905 }
Christopher Tatea732f012017-10-26 17:26:53 -07001906 }
Christopher Tatea732f012017-10-26 17:26:53 -07001907 }
1908
1909 // The expensive check last: validate that the defined package+service is
1910 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001911 final boolean componentPresent;
1912 try {
1913 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1914 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1915 userId) != null);
1916 } catch (RemoteException e) {
1917 throw e.rethrowAsRuntimeException();
1918 }
1919
1920 if (DEBUG) {
1921 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1922 + " componentPresent=" + componentPresent);
1923 }
1924
1925 // Everything else checked out so far, so this is the final yes/no check
1926 return componentPresent;
1927 }
1928
1929 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001930 * Reconcile jobs in the pending queue against available execution contexts.
1931 * A controller can force a job into the pending queue even if it's already running, but
1932 * here is where we decide whether to actually execute it.
1933 */
1934 private void maybeRunPendingJobsLocked() {
1935 if (DEBUG) {
1936 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1937 }
1938 assignJobsToContextsLocked();
1939 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001940 }
1941
Dianne Hackborn807de782016-04-07 17:54:41 -07001942 private int adjustJobPriority(int curPriority, JobStatus job) {
1943 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1944 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001945 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001946 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001947 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001948 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1949 }
1950 }
1951 return curPriority;
1952 }
1953
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001954 private int evaluateJobPriorityLocked(JobStatus job) {
1955 int priority = job.getPriority();
1956 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001957 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001958 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001959 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1960 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001961 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001962 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001963 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001964 }
1965
Christopher Tate7060b042014-06-09 19:50:00 -07001966 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001967 * Takes jobs from pending queue and runs them on available contexts.
1968 * If no contexts are available, preempts lower priority jobs to
1969 * run higher priority ones.
1970 * Lock on mJobs before calling this function.
1971 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001972 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001973 if (DEBUG) {
1974 Slog.d(TAG, printPendingQueue());
1975 }
1976
Dianne Hackborn970510b2016-02-24 16:56:42 -08001977 int memLevel;
1978 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001979 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001980 } catch (RemoteException e) {
1981 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1982 }
1983 switch (memLevel) {
1984 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001985 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001986 break;
1987 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001988 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001989 break;
1990 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001991 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001992 break;
1993 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001994 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001995 break;
1996 }
1997
1998 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
1999 boolean[] act = mTmpAssignAct;
2000 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2001 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002002 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002003 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2004 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002005 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002006 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002007 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002008 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2009 numForeground++;
2010 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002011 }
2012 act[i] = false;
2013 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002014 }
2015 if (DEBUG) {
2016 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2017 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002018 for (int i=0; i<mPendingJobs.size(); i++) {
2019 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002020
2021 // If job is already running, go to next job.
2022 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2023 if (jobRunningContext != -1) {
2024 continue;
2025 }
2026
Dianne Hackborn970510b2016-02-24 16:56:42 -08002027 final int priority = evaluateJobPriorityLocked(nextPending);
2028 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002029
Shreyas Basarge5db09082016-01-07 13:38:29 +00002030 // Find a context for nextPending. The context should be available OR
2031 // it should have lowest priority among all running jobs
2032 // (sharing the same Uid as nextPending)
2033 int minPriority = Integer.MAX_VALUE;
2034 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002035 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2036 JobStatus job = contextIdToJobMap[j];
2037 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00002038 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002039 if ((numActive < mMaxActiveJobs ||
2040 (priority >= JobInfo.PRIORITY_TOP_APP &&
2041 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08002042 (preferredUid == nextPending.getUid() ||
2043 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2044 // This slot is free, and we haven't yet hit the limit on
2045 // concurrent jobs... we can just throw the job in to here.
2046 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002047 break;
2048 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00002049 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08002050 // the context has a preferred Uid or we have reached the limit on
2051 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00002052 continue;
2053 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002054 if (job.getUid() != nextPending.getUid()) {
2055 continue;
2056 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002057 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002058 continue;
2059 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002060 if (minPriority > nextPending.lastEvaluatedPriority) {
2061 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002062 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00002063 }
2064 }
2065 if (minPriorityContextId != -1) {
2066 contextIdToJobMap[minPriorityContextId] = nextPending;
2067 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002068 numActive++;
2069 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2070 numForeground++;
2071 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002072 }
2073 }
2074 if (DEBUG) {
2075 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2076 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002077 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002078 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002079 boolean preservePreferredUid = false;
2080 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002081 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002082 if (js != null) {
2083 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002084 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00002085 }
2086 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07002087 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002088 preservePreferredUid = true;
2089 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002090 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00002091 if (DEBUG) {
2092 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002093 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002094 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002095 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002096 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002097 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002098 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2099 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002100 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002101 if (mPendingJobs.remove(pendingJob)) {
2102 mJobPackageTracker.noteNonpending(pendingJob);
2103 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002104 }
2105 }
2106 if (!preservePreferredUid) {
2107 mActiveServices.get(i).clearPreferredUid();
2108 }
2109 }
2110 }
2111
2112 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2113 for (int i=0; i<map.length; i++) {
2114 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2115 return i;
2116 }
2117 }
2118 return -1;
2119 }
2120
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002121 final class LocalService implements JobSchedulerInternal {
2122
2123 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002124 * The current bucket heartbeat ordinal
2125 */
2126 public long currentHeartbeat() {
2127 return getCurrentHeartbeat();
2128 }
2129
2130 /**
2131 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2132 */
2133 public long nextHeartbeatForBucket(int bucket) {
2134 synchronized (mLock) {
2135 return mNextBucketHeartbeat[bucket];
2136 }
2137 }
2138
2139 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002140 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2141 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2142 * jobs in a long time is immediately runnable even if the app is bucketed into
2143 * an infrequent time allocation.
2144 */
2145 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2146 final int appStandbyBucket) {
2147 if (appStandbyBucket == 0 ||
2148 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2149 // ACTIVE => everything can be run right away
2150 // NEVER => we won't run them anyway, so let them go in the future
2151 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002152 if (DEBUG_STANDBY) {
2153 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2154 + packageName + "/" + userId);
2155 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002156 return 0;
2157 }
2158
Christopher Tated1aebb32018-01-31 13:24:14 -08002159 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2160 if (DEBUG_STANDBY) {
2161 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2162 + packageName + "/" + userId);
2163 }
2164 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002165 }
2166
2167 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002168 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2169 * jobs are always considered pending.
2170 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002171 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002172 public List<JobInfo> getSystemScheduledPendingJobs() {
2173 synchronized (mLock) {
2174 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002175 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2176 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2177 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002178 }
2179 });
2180 return pendingJobs;
2181 }
2182 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002183
2184 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002185 public void cancelJobsForUid(int uid, String reason) {
2186 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2187 }
2188
2189 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002190 public void addBackingUpUid(int uid) {
2191 synchronized (mLock) {
2192 // No need to actually do anything here, since for a full backup the
2193 // activity manager will kill the process which will kill the job (and
2194 // cause it to restart, but now it can't run).
2195 mBackingUpUids.put(uid, uid);
2196 }
2197 }
2198
2199 @Override
2200 public void removeBackingUpUid(int uid) {
2201 synchronized (mLock) {
2202 mBackingUpUids.delete(uid);
2203 // If there are any jobs for this uid, we need to rebuild the pending list
2204 // in case they are now ready to run.
2205 if (mJobs.countJobsForUid(uid) > 0) {
2206 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2207 }
2208 }
2209 }
2210
2211 @Override
2212 public void clearAllBackingUpUids() {
2213 synchronized (mLock) {
2214 if (mBackingUpUids.size() > 0) {
2215 mBackingUpUids.clear();
2216 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2217 }
2218 }
2219 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002220
2221 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002222 public void reportAppUsage(String packageName, int userId) {
2223 JobSchedulerService.this.reportAppUsage(packageName, userId);
2224 }
2225
2226 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002227 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002228 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002229 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002230 }
2231 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002232 }
2233
Shreyas Basarge5db09082016-01-07 13:38:29 +00002234 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002235 * Tracking of app assignments to standby buckets
2236 */
2237 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002238
Christopher Tatea732f012017-10-26 17:26:53 -07002239 // AppIdleStateChangeListener interface for live updates
2240
2241 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002242 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Christopher Tatea732f012017-10-26 17:26:53 -07002243 boolean idle, int bucket) {
2244 final int uid = mLocalPM.getPackageUid(packageName,
2245 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2246 if (uid < 0) {
2247 if (DEBUG_STANDBY) {
2248 Slog.i(TAG, "App idle state change for unknown app "
2249 + packageName + "/" + userId);
2250 }
2251 return;
2252 }
2253
2254 final int bucketIndex = standbyBucketToBucketIndex(bucket);
2255 // update job bookkeeping out of band
2256 BackgroundThread.getHandler().post(() -> {
2257 if (DEBUG_STANDBY) {
2258 Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2259 }
2260 synchronized (mLock) {
Christopher Tate93defea2018-02-20 15:57:17 -08002261 mJobs.forEachJobForSourceUid(uid, job -> {
2262 // double-check uid vs package name to disambiguate shared uids
2263 if (packageName.equals(job.getSourcePackageName())) {
2264 job.setStandbyBucket(bucketIndex);
2265 }
2266 });
Christopher Tatea732f012017-10-26 17:26:53 -07002267 onControllerStateChanged();
2268 }
2269 });
2270 }
2271
2272 @Override
2273 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002274 if (DEBUG_STANDBY) {
2275 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2276 }
2277 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002278 }
Christopher Tated117b292018-01-05 17:32:36 -08002279
2280 @Override
2281 public void onUserInteractionStarted(String packageName, int userId) {
2282 final int uid = mLocalPM.getPackageUid(packageName,
2283 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2284 if (uid < 0) {
2285 // Quietly ignore; the case is already logged elsewhere
2286 return;
2287 }
2288
Amith Yamasani977e11f2018-02-16 11:29:54 -08002289 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2290 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2291 // Too long ago, not worth logging
2292 sinceLast = 0L;
2293 }
Christopher Tated117b292018-01-05 17:32:36 -08002294 final DeferredJobCounter counter = new DeferredJobCounter();
2295 synchronized (mLock) {
2296 mJobs.forEachJobForSourceUid(uid, counter);
2297 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002298 if (counter.numDeferred() > 0 || sinceLast > 0) {
2299 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2300 (BatteryStatsInternal.class);
2301 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2302 }
Christopher Tated117b292018-01-05 17:32:36 -08002303 }
2304 }
2305
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002306 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002307 private int mDeferred = 0;
2308
2309 public int numDeferred() {
2310 return mDeferred;
2311 }
2312
2313 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002314 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002315 if (job.getWhenStandbyDeferred() > 0) {
2316 mDeferred++;
2317 }
2318 }
Christopher Tatea732f012017-10-26 17:26:53 -07002319 }
2320
2321 public static int standbyBucketToBucketIndex(int bucket) {
2322 // Normalize AppStandby constants to indices into our bookkeeping
Amith Yamasaniafbccb72017-11-27 10:44:24 -08002323 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
2324 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
2325 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
2326 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
Christopher Tatea732f012017-10-26 17:26:53 -07002327 else return 0;
2328 }
2329
Christopher Tated1aebb32018-01-31 13:24:14 -08002330 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002331 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2332 UsageStatsManagerInternal usageStats = LocalServices.getService(
2333 UsageStatsManagerInternal.class);
2334 int bucket = usageStats != null
2335 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2336 : 0;
2337
2338 bucket = standbyBucketToBucketIndex(bucket);
2339
2340 if (DEBUG_STANDBY) {
2341 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2342 }
2343 return bucket;
2344 }
2345
2346 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002347 * Binder stub trampoline implementation
2348 */
2349 final class JobSchedulerStub extends IJobScheduler.Stub {
2350 /** Cache determination of whether a given app can persist jobs
2351 * key is uid of the calling app; value is undetermined/true/false
2352 */
2353 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2354
2355 // Enforce that only the app itself (or shared uid participant) can schedule a
2356 // job that runs one of the app's services, as well as verifying that the
2357 // named service properly requires the BIND_JOB_SERVICE permission
2358 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002359 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002360 final ComponentName service = job.getService();
2361 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002362 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002363 PackageManager.MATCH_DIRECT_BOOT_AWARE
2364 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002365 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002366 if (si == null) {
2367 throw new IllegalArgumentException("No such service " + service);
2368 }
Christopher Tate7060b042014-06-09 19:50:00 -07002369 if (si.applicationInfo.uid != uid) {
2370 throw new IllegalArgumentException("uid " + uid +
2371 " cannot schedule job in " + service.getPackageName());
2372 }
2373 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2374 throw new IllegalArgumentException("Scheduled service " + service
2375 + " does not require android.permission.BIND_JOB_SERVICE permission");
2376 }
Christopher Tate5568f542014-06-18 13:53:31 -07002377 } catch (RemoteException e) {
2378 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002379 }
2380 }
2381
2382 private boolean canPersistJobs(int pid, int uid) {
2383 // If we get this far we're good to go; all we need to do now is check
2384 // whether the app is allowed to persist its scheduled work.
2385 final boolean canPersist;
2386 synchronized (mPersistCache) {
2387 Boolean cached = mPersistCache.get(uid);
2388 if (cached != null) {
2389 canPersist = cached.booleanValue();
2390 } else {
2391 // Persisting jobs is tantamount to running at boot, so we permit
2392 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2393 // permission
2394 int result = getContext().checkPermission(
2395 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2396 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2397 mPersistCache.put(uid, canPersist);
2398 }
2399 }
2400 return canPersist;
2401 }
2402
Makoto Onuki959acb52018-01-26 14:10:03 -08002403 private void validateJobFlags(JobInfo job, int callingUid) {
2404 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2405 getContext().enforceCallingOrSelfPermission(
2406 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2407 }
2408 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2409 if (callingUid != Process.SYSTEM_UID) {
2410 throw new SecurityException("Job has invalid flags");
2411 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002412 if (job.isPeriodic()) {
2413 Slog.wtf(TAG, "Periodic jobs mustn't have"
2414 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002415 }
2416 }
2417 }
2418
Christopher Tate7060b042014-06-09 19:50:00 -07002419 // IJobScheduler implementation
2420 @Override
2421 public int schedule(JobInfo job) throws RemoteException {
2422 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002423 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002424 }
2425 final int pid = Binder.getCallingPid();
2426 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002427 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002428
2429 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002430 if (job.isPersisted()) {
2431 if (!canPersistJobs(pid, uid)) {
2432 throw new IllegalArgumentException("Error: requested job be persisted without"
2433 + " holding RECEIVE_BOOT_COMPLETED permission.");
2434 }
2435 }
Christopher Tate7060b042014-06-09 19:50:00 -07002436
Makoto Onuki959acb52018-01-26 14:10:03 -08002437 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002438
Christopher Tate7060b042014-06-09 19:50:00 -07002439 long ident = Binder.clearCallingIdentity();
2440 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002441 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2442 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002443 } finally {
2444 Binder.restoreCallingIdentity(ident);
2445 }
2446 }
2447
2448 // IJobScheduler implementation
2449 @Override
2450 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2451 if (DEBUG) {
2452 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2453 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002454 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002455 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002456
2457 enforceValidJobRequest(uid, job);
2458 if (job.isPersisted()) {
2459 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2460 }
2461 if (work == null) {
2462 throw new NullPointerException("work is null");
2463 }
2464
Makoto Onuki959acb52018-01-26 14:10:03 -08002465 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002466
2467 long ident = Binder.clearCallingIdentity();
2468 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002469 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2470 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002471 } finally {
2472 Binder.restoreCallingIdentity(ident);
2473 }
2474 }
2475
2476 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002477 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002478 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002479 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002480 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002481 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002482 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002483 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002484
2485 if (packageName == null) {
2486 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002487 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002488
2489 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2490 android.Manifest.permission.UPDATE_DEVICE_STATS);
2491 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2492 throw new SecurityException("Caller uid " + callerUid
2493 + " not permitted to schedule jobs for other apps");
2494 }
2495
Makoto Onuki959acb52018-01-26 14:10:03 -08002496 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002497
Shreyas Basarge968ac752016-01-11 23:09:26 +00002498 long ident = Binder.clearCallingIdentity();
2499 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002500 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002501 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002502 } finally {
2503 Binder.restoreCallingIdentity(ident);
2504 }
2505 }
2506
2507 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002508 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2509 final int uid = Binder.getCallingUid();
2510
2511 long ident = Binder.clearCallingIdentity();
2512 try {
2513 return JobSchedulerService.this.getPendingJobs(uid);
2514 } finally {
2515 Binder.restoreCallingIdentity(ident);
2516 }
2517 }
2518
2519 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002520 public JobInfo getPendingJob(int jobId) throws RemoteException {
2521 final int uid = Binder.getCallingUid();
2522
2523 long ident = Binder.clearCallingIdentity();
2524 try {
2525 return JobSchedulerService.this.getPendingJob(uid, jobId);
2526 } finally {
2527 Binder.restoreCallingIdentity(ident);
2528 }
2529 }
2530
2531 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002532 public void cancelAll() throws RemoteException {
2533 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002534 long ident = Binder.clearCallingIdentity();
2535 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002536 JobSchedulerService.this.cancelJobsForUid(uid,
2537 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002538 } finally {
2539 Binder.restoreCallingIdentity(ident);
2540 }
2541 }
2542
2543 @Override
2544 public void cancel(int jobId) throws RemoteException {
2545 final int uid = Binder.getCallingUid();
2546
2547 long ident = Binder.clearCallingIdentity();
2548 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002549 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002550 } finally {
2551 Binder.restoreCallingIdentity(ident);
2552 }
2553 }
2554
2555 /**
2556 * "dumpsys" infrastructure
2557 */
2558 @Override
2559 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002560 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002561
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002562 int filterUid = -1;
2563 boolean proto = false;
2564 if (!ArrayUtils.isEmpty(args)) {
2565 int opti = 0;
2566 while (opti < args.length) {
2567 String arg = args[opti];
2568 if ("-h".equals(arg)) {
2569 dumpHelp(pw);
2570 return;
2571 } else if ("-a".equals(arg)) {
2572 // Ignore, we always dump all.
2573 } else if ("--proto".equals(arg)) {
2574 proto = true;
2575 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2576 pw.println("Unknown option: " + arg);
2577 return;
2578 } else {
2579 break;
2580 }
2581 opti++;
2582 }
2583 if (opti < args.length) {
2584 String pkg = args[opti];
2585 try {
2586 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2587 PackageManager.MATCH_ANY_USER);
2588 } catch (NameNotFoundException ignored) {
2589 pw.println("Invalid package: " + pkg);
2590 return;
2591 }
2592 }
2593 }
2594
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002595 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07002596 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002597 if (proto) {
2598 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2599 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002600 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
2601 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002602 }
Christopher Tate7060b042014-06-09 19:50:00 -07002603 } finally {
2604 Binder.restoreCallingIdentity(identityToken);
2605 }
2606 }
Christopher Tate5d346052016-03-08 12:56:08 -08002607
2608 @Override
2609 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002610 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002611 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002612 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002613 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002614 };
2615
Christopher Tate5d346052016-03-08 12:56:08 -08002616 // Shell command infrastructure: run the given job immediately
2617 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2618 if (DEBUG) {
2619 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2620 + " " + jobId + " f=" + force);
2621 }
2622
2623 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002624 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2625 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002626 if (uid < 0) {
2627 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2628 }
2629
2630 synchronized (mLock) {
2631 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2632 if (js == null) {
2633 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2634 }
2635
2636 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2637 if (!js.isConstraintsSatisfied()) {
2638 js.overrideState = 0;
2639 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2640 }
2641
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002642 queueReadyJobsForExecutionLocked();
2643 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002644 }
2645 } catch (RemoteException e) {
2646 // can't happen
2647 }
2648 return 0;
2649 }
2650
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002651 // Shell command infrastructure: immediately timeout currently executing jobs
2652 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2653 boolean hasJobId, int jobId) {
2654 if (DEBUG) {
2655 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2656 }
2657
2658 synchronized (mLock) {
2659 boolean foundSome = false;
2660 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002661 final JobServiceContext jc = mActiveServices.get(i);
2662 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002663 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002664 foundSome = true;
2665 pw.print("Timing out: ");
2666 js.printUniqueId(pw);
2667 pw.print(" ");
2668 pw.println(js.getServiceComponent().flattenToShortString());
2669 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002670 }
2671 if (!foundSome) {
2672 pw.println("No matching executing jobs found.");
2673 }
2674 }
2675 return 0;
2676 }
2677
Christopher Tate8c67d122017-09-29 16:54:26 -07002678 // Shell command infrastructure: cancel a scheduled job
2679 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2680 boolean hasJobId, int jobId) {
2681 if (DEBUG) {
2682 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2683 }
2684
2685 int pkgUid = -1;
2686 try {
2687 IPackageManager pm = AppGlobals.getPackageManager();
2688 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2689 } catch (RemoteException e) { /* can't happen */ }
2690
2691 if (pkgUid < 0) {
2692 pw.println("Package " + pkgName + " not found.");
2693 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2694 }
2695
2696 if (!hasJobId) {
2697 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2698 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2699 pw.println("No matching jobs found.");
2700 }
2701 } else {
2702 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002703 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002704 pw.println("No matching job found.");
2705 }
2706 }
2707
2708 return 0;
2709 }
2710
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002711 void setMonitorBattery(boolean enabled) {
2712 synchronized (mLock) {
2713 if (mBatteryController != null) {
2714 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2715 }
2716 }
2717 }
2718
2719 int getBatterySeq() {
2720 synchronized (mLock) {
2721 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2722 }
2723 }
2724
2725 boolean getBatteryCharging() {
2726 synchronized (mLock) {
2727 return mBatteryController != null
2728 ? mBatteryController.getTracker().isOnStablePower() : false;
2729 }
2730 }
2731
2732 boolean getBatteryNotLow() {
2733 synchronized (mLock) {
2734 return mBatteryController != null
2735 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2736 }
2737 }
2738
Dianne Hackborn532ea262017-03-17 17:50:55 -07002739 int getStorageSeq() {
2740 synchronized (mLock) {
2741 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2742 }
2743 }
2744
2745 boolean getStorageNotLow() {
2746 synchronized (mLock) {
2747 return mStorageController != null
2748 ? mStorageController.getTracker().isStorageNotLow() : false;
2749 }
2750 }
2751
Christopher Tatea732f012017-10-26 17:26:53 -07002752 long getCurrentHeartbeat() {
2753 synchronized (mLock) {
2754 return mHeartbeat;
2755 }
2756 }
2757
Christopher Tated1aebb32018-01-31 13:24:14 -08002758 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07002759 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2760 try {
2761 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2762 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2763 if (uid < 0) {
2764 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2765 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2766 }
2767
2768 synchronized (mLock) {
2769 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2770 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2771 if (js == null) {
2772 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2773 pw.print("/jid"); pw.print(jobId); pw.println(")");
2774 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2775 }
2776
2777 boolean printed = false;
2778 if (mPendingJobs.contains(js)) {
2779 pw.print("pending");
2780 printed = true;
2781 }
2782 if (isCurrentlyActiveLocked(js)) {
2783 if (printed) {
2784 pw.print(" ");
2785 }
2786 printed = true;
2787 pw.println("active");
2788 }
2789 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2790 if (printed) {
2791 pw.print(" ");
2792 }
2793 printed = true;
2794 pw.println("user-stopped");
2795 }
2796 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2797 if (printed) {
2798 pw.print(" ");
2799 }
2800 printed = true;
2801 pw.println("backing-up");
2802 }
2803 boolean componentPresent = false;
2804 try {
2805 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2806 js.getServiceComponent(),
2807 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2808 js.getUserId()) != null);
2809 } catch (RemoteException e) {
2810 }
2811 if (!componentPresent) {
2812 if (printed) {
2813 pw.print(" ");
2814 }
2815 printed = true;
2816 pw.println("no-component");
2817 }
2818 if (js.isReady()) {
2819 if (printed) {
2820 pw.print(" ");
2821 }
2822 printed = true;
2823 pw.println("ready");
2824 }
2825 if (!printed) {
2826 pw.print("waiting");
2827 }
2828 pw.println();
2829 }
2830 } catch (RemoteException e) {
2831 // can't happen
2832 }
2833 return 0;
2834 }
2835
Christopher Tated1aebb32018-01-31 13:24:14 -08002836 // Shell command infrastructure
2837 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
2838 if (numBeats < 1) {
2839 pw.println(getCurrentHeartbeat());
2840 return 0;
2841 }
2842
2843 pw.print("Advancing standby heartbeat by ");
2844 pw.println(numBeats);
2845 synchronized (mLock) {
2846 advanceHeartbeatLocked(numBeats);
2847 }
2848 return 0;
2849 }
2850
Shreyas Basarge5db09082016-01-07 13:38:29 +00002851 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2852 StringBuilder s = new StringBuilder(initial + ": ");
2853 for (int i=0; i<map.length; i++) {
2854 s.append("(")
2855 .append(map[i] == null? -1: map[i].getJobId())
2856 .append(map[i] == null? -1: map[i].getUid())
2857 .append(")" );
2858 }
2859 return s.toString();
2860 }
2861
2862 private String printPendingQueue() {
2863 StringBuilder s = new StringBuilder("Pending queue: ");
2864 Iterator<JobStatus> it = mPendingJobs.iterator();
2865 while (it.hasNext()) {
2866 JobStatus js = it.next();
2867 s.append("(")
2868 .append(js.getJob().getId())
2869 .append(", ")
2870 .append(js.getUid())
2871 .append(") ");
2872 }
2873 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07002874 }
Christopher Tate7060b042014-06-09 19:50:00 -07002875
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002876 static void dumpHelp(PrintWriter pw) {
2877 pw.println("Job Scheduler (jobscheduler) dump options:");
2878 pw.println(" [-h] [package] ...");
2879 pw.println(" -h: print this help");
2880 pw.println(" [package] is an optional package name to limit the output to.");
2881 }
2882
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002883 /** Sort jobs by caller UID, then by Job ID. */
2884 private static void sortJobs(List<JobStatus> jobs) {
2885 Collections.sort(jobs, new Comparator<JobStatus>() {
2886 @Override
2887 public int compare(JobStatus o1, JobStatus o2) {
2888 int uid1 = o1.getUid();
2889 int uid2 = o2.getUid();
2890 int id1 = o1.getJobId();
2891 int id2 = o2.getJobId();
2892 if (uid1 != uid2) {
2893 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002894 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002895 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002896 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002897 });
2898 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002899
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002900 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002901 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07002902 final long nowElapsed = sElapsedRealtimeClock.millis();
2903 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002904 final Predicate<JobStatus> predicate = (js) -> {
2905 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
2906 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
2907 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002908 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002909 mConstants.dump(pw);
2910 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07002911 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002912 pw.print("Registered ");
2913 pw.print(mJobs.size());
2914 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07002915 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002916 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002917 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002918 for (JobStatus job : jobs) {
2919 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2920 pw.println(job.toShortStringExceptUniqueId());
2921
2922 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002923 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002924 continue;
2925 }
2926
Dianne Hackborn6d068262017-05-16 13:14:37 -07002927 job.dump(pw, " ", true, nowElapsed);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002928 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002929 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002930 pw.print(" (job=");
2931 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002932 pw.print(" user=");
2933 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002934 pw.print(" !pending=");
2935 pw.print(!mPendingJobs.contains(job));
2936 pw.print(" !active=");
2937 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002938 pw.print(" !backingup=");
2939 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002940 pw.print(" comp=");
2941 boolean componentPresent = false;
2942 try {
2943 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2944 job.getServiceComponent(),
2945 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2946 job.getUserId()) != null);
2947 } catch (RemoteException e) {
2948 }
2949 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002950 pw.println(")");
2951 }
Christopher Tate7060b042014-06-09 19:50:00 -07002952 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002953 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002954 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002955 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002956 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002957 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
2958 pw.increaseIndent();
2959 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
2960 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07002961 }
2962 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002963 pw.println("Uid priority overrides:");
2964 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002965 int uid = mUidPriorityOverride.keyAt(i);
2966 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2967 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2968 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2969 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002970 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002971 if (mBackingUpUids.size() > 0) {
2972 pw.println();
2973 pw.println("Backing up uids:");
2974 boolean first = true;
2975 for (int i = 0; i < mBackingUpUids.size(); i++) {
2976 int uid = mBackingUpUids.keyAt(i);
2977 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2978 if (first) {
2979 pw.print(" ");
2980 first = false;
2981 } else {
2982 pw.print(", ");
2983 }
2984 pw.print(UserHandle.formatUid(uid));
2985 }
2986 }
2987 pw.println();
2988 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002989 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002990 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002991 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002992 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2993 pw.println();
2994 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002995 pw.println("Pending queue:");
2996 for (int i=0; i<mPendingJobs.size(); i++) {
2997 JobStatus job = mPendingJobs.get(i);
2998 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2999 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003000 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003001 int priority = evaluateJobPriorityLocked(job);
3002 if (priority != JobInfo.PRIORITY_DEFAULT) {
3003 pw.print(" Evaluated priority: "); pw.println(priority);
3004 }
3005 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003006 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003007 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003008 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003009 }
Christopher Tate7060b042014-06-09 19:50:00 -07003010 pw.println();
3011 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003012 for (int i=0; i<mActiveServices.size(); i++) {
3013 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003014 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003015 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003016 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003017 if (jsc.mStoppedReason != null) {
3018 pw.print("inactive since ");
3019 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3020 pw.print(", stopped because: ");
3021 pw.println(jsc.mStoppedReason);
3022 } else {
3023 pw.println("inactive");
3024 }
Christopher Tate7060b042014-06-09 19:50:00 -07003025 continue;
3026 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003027 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003028 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003029 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003030 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003031 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003032 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003033 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003034 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003035 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08003036 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003037 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003038 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003039 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003040 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003041 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3042 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003043 }
3044 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003045 if (filterUid == -1) {
3046 pw.println();
3047 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3048 pw.print("mReportedActive="); pw.println(mReportedActive);
3049 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3050 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003051 pw.println();
3052 pw.print("PersistStats: ");
3053 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003054 }
3055 pw.println();
3056 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003057
3058 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3059 ProtoOutputStream proto = new ProtoOutputStream(fd);
3060 final int filterUidFinal = UserHandle.getAppId(filterUid);
3061 final long nowElapsed = sElapsedRealtimeClock.millis();
3062 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003063 final Predicate<JobStatus> predicate = (js) -> {
3064 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3065 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3066 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003067
3068 synchronized (mLock) {
3069 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
3070 for (int u : mStartedUsers) {
3071 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3072 }
3073 if (mJobs.size() > 0) {
3074 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3075 sortJobs(jobs);
3076 for (JobStatus job : jobs) {
3077 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3078 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3079
3080 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003081 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003082 continue;
3083 }
3084
3085 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3086
3087 // isReadyToBeExecuted
3088 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3089 job.isReady());
3090 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3091 ArrayUtils.contains(mStartedUsers, job.getUserId()));
3092 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3093 mPendingJobs.contains(job));
3094 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3095 isCurrentlyActiveLocked(job));
3096 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3097 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3098 boolean componentPresent = false;
3099 try {
3100 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3101 job.getServiceComponent(),
3102 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3103 job.getUserId()) != null);
3104 } catch (RemoteException e) {
3105 }
3106 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3107 componentPresent);
3108
3109 proto.end(rjToken);
3110 }
3111 }
3112 for (StateController controller : mControllers) {
3113 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003114 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003115 }
3116 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3117 int uid = mUidPriorityOverride.keyAt(i);
3118 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3119 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3120 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3121 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3122 mUidPriorityOverride.valueAt(i));
3123 proto.end(pToken);
3124 }
3125 }
3126 for (int i = 0; i < mBackingUpUids.size(); i++) {
3127 int uid = mBackingUpUids.keyAt(i);
3128 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3129 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3130 }
3131 }
3132
3133 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3134 filterUidFinal);
3135 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3136 filterUidFinal);
3137
3138 for (JobStatus job : mPendingJobs) {
3139 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3140
3141 job.writeToShortProto(proto, PendingJob.INFO);
3142 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3143 int priority = evaluateJobPriorityLocked(job);
3144 if (priority != JobInfo.PRIORITY_DEFAULT) {
3145 proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3146 }
3147 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3148
3149 proto.end(pjToken);
3150 }
3151 for (JobServiceContext jsc : mActiveServices) {
3152 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3153 final JobStatus job = jsc.getRunningJobLocked();
3154
3155 if (job == null) {
3156 final long ijToken = proto.start(ActiveJob.INACTIVE);
3157
3158 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3159 nowElapsed - jsc.mStoppedTime);
3160 if (jsc.mStoppedReason != null) {
3161 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3162 jsc.mStoppedReason);
3163 }
3164
3165 proto.end(ijToken);
3166 } else {
3167 final long rjToken = proto.start(ActiveJob.RUNNING);
3168
3169 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3170
3171 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3172 nowElapsed - jsc.getExecutionStartTimeElapsed());
3173 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3174 jsc.getTimeoutElapsed() - nowElapsed);
3175
3176 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3177
3178 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3179 if (priority != JobInfo.PRIORITY_DEFAULT) {
3180 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3181 }
3182
3183 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3184 nowUptime - job.madeActive);
3185 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3186 job.madeActive - job.madePending);
3187
3188 proto.end(rjToken);
3189 }
3190 proto.end(ajToken);
3191 }
3192 if (filterUid == -1) {
3193 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3194 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3195 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3196 }
3197 }
3198
3199 proto.flush();
3200 }
Christopher Tate7060b042014-06-09 19:50:00 -07003201}