blob: 66817fad4e5b2729e8d8270a54d9c5fcbfb11ddb [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;
Tej Singh33a412b2018-03-16 18:43:59 -070032import android.app.job.JobProtoEnums;
Christopher Tate7060b042014-06-09 19:50:00 -070033import android.app.job.JobScheduler;
34import android.app.job.JobService;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070035import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080036import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070037import android.app.usage.UsageStatsManagerInternal;
38import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070039import android.content.BroadcastReceiver;
40import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070041import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070042import android.content.Context;
43import android.content.Intent;
44import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070045import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070046import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060047import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070048import android.content.pm.PackageManagerInternal;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070049import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070050import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070051import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070052import android.os.BatteryStats;
Amith Yamasani977e11f2018-02-16 11:29:54 -080053import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070054import android.os.Binder;
55import android.os.Handler;
56import android.os.Looper;
57import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070058import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070059import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080060import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070061import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070062import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070063import android.os.SystemClock;
64import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070065import android.os.UserManagerInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070066import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080067import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070068import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070069import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070070import android.util.Slog;
71import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080072import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080073import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080074import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080075import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080076
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070077import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070078import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070079import com.android.internal.app.procstats.ProcessStats;
Christopher Tatea732f012017-10-26 17:26:53 -070080import com.android.internal.os.BackgroundThread;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070081import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060082import com.android.internal.util.DumpUtils;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070083import com.android.internal.util.IndentingPrintWriter;
Makoto Onuki15407842018-01-19 14:23:11 -080084import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070085import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080086import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070087import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080088import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080089import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
90import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Makoto Onukib5d5e972018-02-20 14:44:20 -080091import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070092import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070093import com.android.server.job.controllers.BatteryController;
94import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080095import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070096import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070097import com.android.server.job.controllers.IdleController;
98import com.android.server.job.controllers.JobStatus;
99import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700100import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700101import com.android.server.job.controllers.TimeController;
102
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700103import libcore.util.EmptyArray;
104
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700105import java.io.FileDescriptor;
106import java.io.PrintWriter;
107import java.time.Clock;
108import java.util.ArrayList;
109import java.util.Arrays;
110import java.util.Collections;
111import java.util.Comparator;
Christopher Tate325768c2018-03-07 16:07:56 -0800112import java.util.HashMap;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700113import java.util.Iterator;
114import java.util.List;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700115import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800116import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700117
Christopher Tate7060b042014-06-09 19:50:00 -0700118/**
119 * Responsible for taking jobs representing work to be performed by a client app, and determining
120 * based on the criteria specified when that job should be run against the client application's
121 * endpoint.
122 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
123 * about constraints, or the state of active jobs. It receives callbacks from the various
124 * controllers and completed jobs and operates accordingly.
125 *
126 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
127 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
128 * @hide
129 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800130public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700131 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700132 public static final String TAG = "JobScheduler";
133 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700134 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800135
Dianne Hackborn970510b2016-02-24 16:56:42 -0800136 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700137 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800138 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800139 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800140 /** The maximum number of jobs that we allow an unprivileged app to schedule */
141 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700142
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700143 @VisibleForTesting
144 public static Clock sSystemClock = Clock.systemUTC();
145 @VisibleForTesting
146 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
147 @VisibleForTesting
148 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800149
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800150 /** Global local for all job scheduler state. */
151 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700152 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700153 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700154 /** Tracking the standby bucket state of each app */
155 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700156 /** Tracking amount of time each package runs for. */
157 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700158
159 static final int MSG_JOB_EXPIRED = 0;
160 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700161 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000162 static final int MSG_CHECK_JOB_GREEDY = 3;
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800163 static final int MSG_UID_STATE_CHANGED = 4;
164 static final int MSG_UID_GONE = 5;
165 static final int MSG_UID_ACTIVE = 6;
166 static final int MSG_UID_IDLE = 7;
Christopher Tate7060b042014-06-09 19:50:00 -0700167
Christopher Tate7060b042014-06-09 19:50:00 -0700168 /**
169 * Track Services that have currently active or pending jobs. The index is provided by
170 * {@link JobStatus#getServiceToken()}
171 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700172 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700173
Christopher Tate7060b042014-06-09 19:50:00 -0700174 /** List of controllers that will notify this service of updates to jobs. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700175 private final List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800176 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700177 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700178 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700179 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700180 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700181 private final DeviceIdleJobsController mDeviceIdleJobsController;
182
Christopher Tate7060b042014-06-09 19:50:00 -0700183 /**
184 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
185 * when ready to execute them.
186 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700187 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700188
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700189 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700190
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700191 final JobHandler mHandler;
192 final JobSchedulerStub mJobSchedulerStub;
193
Christopher Tatea732f012017-10-26 17:26:53 -0700194 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800195 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700196 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800197 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800198 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800199 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700200
201 /**
202 * Set to true once we are allowed to run third party apps.
203 */
204 boolean mReadyToRock;
205
Christopher Tate7060b042014-06-09 19:50:00 -0700206 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800207 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800208 */
209 boolean mReportedActive;
210
211 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800212 * Are we currently in device-wide standby parole?
213 */
214 volatile boolean mInParole;
215
216 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800217 * Current limit on the number of concurrent JobServiceContext entries we want to
218 * keep actively running a job.
219 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700220 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800221
222 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800223 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800224 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800225 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
226
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700227 /**
228 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
229 */
230 final SparseIntArray mBackingUpUids = new SparseIntArray();
231
Christopher Tatea732f012017-10-26 17:26:53 -0700232 /**
233 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
234 * next become runnable. Index into this array is by normalized bucket:
235 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
236 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
237 * and NEVER apps don't get them at all.
238 */
239 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
240 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800241 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
242
Christopher Tate325768c2018-03-07 16:07:56 -0800243 /**
244 * Named indices into the STANDBY_BEATS array, for clarity in referring to
245 * specific buckets' bookkeeping.
246 */
247 static final int ACTIVE_INDEX = 0;
248 static final int WORKING_INDEX = 1;
249 static final int FREQUENT_INDEX = 2;
250 static final int RARE_INDEX = 3;
251
252 /**
253 * Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
254 * rather than rely on Usage Stats' timestamps, because heartbeat time can be
255 * manipulated for testing purposes and we need job runnability to track that rather
256 * than real time.
257 *
258 * Outer SparseArray slices by user handle; inner map of package name to heartbeat
259 * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
260 * and it will be accessed in a known-hot code path.
261 */
262 final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
263
Christopher Tated1aebb32018-01-31 13:24:14 -0800264 static final String HEARTBEAT_TAG = "*job.heartbeat*";
265 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700266
Dianne Hackborn970510b2016-02-24 16:56:42 -0800267 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
268
269 /**
270 * This array essentially stores the state of mActiveServices array.
271 * The ith index stores the job present on the ith JobServiceContext.
272 * We manipulate this array until we arrive at what jobs should be running on
273 * what JobServiceContext.
274 */
275 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
276 /**
277 * Indicates whether we need to act on this jobContext id
278 */
279 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
280 /**
281 * The uid whose jobs we would like to assign to a context.
282 */
283 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800284
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700285 private class ConstantsObserver extends ContentObserver {
286 private ContentResolver mResolver;
287
288 public ConstantsObserver(Handler handler) {
289 super(handler);
290 }
291
292 public void start(ContentResolver resolver) {
293 mResolver = resolver;
294 mResolver.registerContentObserver(Settings.Global.getUriFor(
295 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
296 updateConstants();
297 }
298
299 @Override
300 public void onChange(boolean selfChange, Uri uri) {
301 updateConstants();
302 }
303
304 private void updateConstants() {
305 synchronized (mLock) {
306 try {
307 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
308 Settings.Global.JOB_SCHEDULER_CONSTANTS));
309 } catch (IllegalArgumentException e) {
310 // Failed to parse the settings string, log this and move on
311 // with defaults.
312 Slog.e(TAG, "Bad jobscheduler settings", e);
313 }
314 }
315
316 // Reset the heartbeat alarm based on the new heartbeat duration
317 setNextHeartbeatAlarm();
318 }
319 }
320
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800321 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700322 * All times are in milliseconds. These constants are kept synchronized with the system
323 * global Settings. Any access to this class or its fields should be done while
324 * holding the JobSchedulerService.mLock lock.
325 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700326 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700327 // Key names stored in the settings value.
328 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
329 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800330 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700331 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700332 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
333 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
334 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
335 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
336 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
337 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
338 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
339 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
340 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
341 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700342 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
343 = "max_standard_reschedule_count";
344 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
345 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
346 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700347 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
348 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
349 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
350 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700351 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
352 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700353
354 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
355 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800356 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700357 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700358 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
359 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
360 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
361 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
362 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
363 private static final int DEFAULT_FG_JOB_COUNT = 4;
364 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
365 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700366 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700367 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700368 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
369 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
370 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
371 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700372 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000373 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
374 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700375 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700376 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
377 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700378
379 /**
380 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
381 * early.
382 */
383 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
384 /**
385 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
386 * things early.
387 */
388 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
389 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800390 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
391 * schedule things early.
392 */
393 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
394 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700395 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
396 * schedule things early.
397 */
398 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
399 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700400 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
401 * things early. 1 == Run connectivity jobs as soon as ready.
402 */
403 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
404 /**
405 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
406 * schedule things early.
407 */
408 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
409 /**
410 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
411 * running some work early. This (and thus the other min counts) is now set to 1, to
412 * prevent any batching at this level. Since we now do batching through doze, that is
413 * a much better mechanism.
414 */
415 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
416 /**
417 * This is the job execution factor that is considered to be heavy use of the system.
418 */
419 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
420 /**
421 * This is the job execution factor that is considered to be moderate use of the system.
422 */
423 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
424 /**
425 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
426 */
427 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
428 /**
429 * The maximum number of background jobs we allow when the system is in a normal
430 * memory state.
431 */
432 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
433 /**
434 * The maximum number of background jobs we allow when the system is in a moderate
435 * memory state.
436 */
437 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
438 /**
439 * The maximum number of background jobs we allow when the system is in a low
440 * memory state.
441 */
442 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
443 /**
444 * The maximum number of background jobs we allow when the system is in a critical
445 * memory state.
446 */
447 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700448 /**
449 * The maximum number of times we allow a job to have itself rescheduled before
450 * giving up on it, for standard jobs.
451 */
452 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
453 /**
454 * The maximum number of times we allow a job to have itself rescheduled before
455 * giving up on it, for jobs that are executing work.
456 */
457 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
458 /**
459 * The minimum backoff time to allow for linear backoff.
460 */
461 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
462 /**
463 * The minimum backoff time to allow for exponential backoff.
464 */
465 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700466 /**
467 * How often we recalculate runnability based on apps' standby bucket assignment.
468 * This should be prime relative to common time interval lengths such as a quarter-
469 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
470 */
471 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700472 /**
473 * Mapping: standby bucket -> number of heartbeats between each sweep of that
474 * bucket's jobs.
475 *
476 * Bucket assignments as recorded in the JobStatus objects are normalized to be
477 * indices into this array, rather than the raw constants used
478 * by AppIdleHistory.
479 */
480 final int[] STANDBY_BEATS = {
481 0,
482 DEFAULT_STANDBY_WORKING_BEATS,
483 DEFAULT_STANDBY_FREQUENT_BEATS,
484 DEFAULT_STANDBY_RARE_BEATS
485 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700486 /**
487 * The fraction of a job's running window that must pass before we
488 * consider running it when the network is congested.
489 */
490 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
491 /**
492 * The fraction of a prefetch job's running window that must pass before
493 * we consider matching it against a metered network.
494 */
495 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700496
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700497 private final KeyValueListParser mParser = new KeyValueListParser(',');
498
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700499 void updateConstantsLocked(String value) {
500 try {
501 mParser.setString(value);
502 } catch (Exception e) {
503 // Failed to parse the settings string, log this and move on
504 // with defaults.
505 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700506 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800507
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700508 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
509 DEFAULT_MIN_IDLE_COUNT);
510 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
511 DEFAULT_MIN_CHARGING_COUNT);
512 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
513 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
514 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
515 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
516 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
517 DEFAULT_MIN_CONNECTIVITY_COUNT);
518 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
519 DEFAULT_MIN_CONTENT_COUNT);
520 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
521 DEFAULT_MIN_READY_JOBS_COUNT);
522 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
523 DEFAULT_HEAVY_USE_FACTOR);
524 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
525 DEFAULT_MODERATE_USE_FACTOR);
526 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
527 DEFAULT_FG_JOB_COUNT);
528 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
529 DEFAULT_BG_NORMAL_JOB_COUNT);
530 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
531 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
532 }
533 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
534 DEFAULT_BG_MODERATE_JOB_COUNT);
535 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
536 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
537 }
538 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
539 DEFAULT_BG_LOW_JOB_COUNT);
540 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
541 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
542 }
543 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
544 DEFAULT_BG_CRITICAL_JOB_COUNT);
545 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
546 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
547 }
548 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
549 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
550 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
551 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
552 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
553 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
554 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
555 DEFAULT_MIN_EXP_BACKOFF_TIME);
556 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
557 DEFAULT_STANDBY_HEARTBEAT_TIME);
Christopher Tate325768c2018-03-07 16:07:56 -0800558 STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700559 DEFAULT_STANDBY_WORKING_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800560 STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700561 DEFAULT_STANDBY_FREQUENT_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800562 STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700563 DEFAULT_STANDBY_RARE_BEATS);
564 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
565 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
566 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
567 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700568 }
569
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700570 void dump(IndentingPrintWriter pw) {
571 pw.println("Settings:");
572 pw.increaseIndent();
573 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
574 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
575 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
576 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
577 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
578 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
579 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
580 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
581 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
582 pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
583 pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
584 pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
585 pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
586 pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
587 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
588 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
589 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
590 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
591 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
592 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700593 pw.print(STANDBY_BEATS[0]);
594 for (int i = 1; i < STANDBY_BEATS.length; i++) {
595 pw.print(", ");
596 pw.print(STANDBY_BEATS[i]);
597 }
598 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700599 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
600 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
601 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700602 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800603
604 void dump(ProtoOutputStream proto, long fieldId) {
605 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800606 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
607 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
608 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
609 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
610 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
611 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
612 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
613 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
614 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
615 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
616 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
617 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
618 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
619 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
620 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
621 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
622 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
623 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
624 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800625 for (int period : STANDBY_BEATS) {
626 proto.write(ConstantsProto.STANDBY_BEATS, period);
627 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700628 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
629 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800630 proto.end(token);
631 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700632 }
633
634 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700635 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700636
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700637 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
638 if (o1.enqueueTime < o2.enqueueTime) {
639 return -1;
640 }
641 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
642 };
643
644 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
645 int where = Collections.binarySearch(array, newItem, comparator);
646 if (where < 0) {
647 where = ~where;
648 }
649 array.add(where, newItem);
650 }
651
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700652 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700653 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
654 * still clean up. On reinstall the package will have a new uid.
655 */
656 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
657 @Override
658 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700659 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700660 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700661 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700662 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700663 final String pkgName = getPackageName(intent);
664 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
665
Christopher Tateee7805b2016-07-15 16:56:56 -0700666 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700667 // Purge the app's jobs if the whole package was just disabled. When this is
668 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700669 if (pkgName != null && pkgUid != -1) {
670 final String[] changedComponents = intent.getStringArrayExtra(
671 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
672 if (changedComponents != null) {
673 for (String component : changedComponents) {
674 if (component.equals(pkgName)) {
675 if (DEBUG) {
676 Slog.d(TAG, "Package state change: " + pkgName);
677 }
678 try {
679 final int userId = UserHandle.getUserId(pkgUid);
680 IPackageManager pm = AppGlobals.getPackageManager();
681 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
682 if (state == COMPONENT_ENABLED_STATE_DISABLED
683 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
684 if (DEBUG) {
685 Slog.d(TAG, "Removing jobs for package " + pkgName
686 + " in user " + userId);
687 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700688 cancelJobsForPackageAndUid(pkgName, pkgUid,
689 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700690 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700691 } catch (RemoteException|IllegalArgumentException e) {
692 /*
693 * IllegalArgumentException means that the package doesn't exist.
694 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
695 * behind outright uninstall, so by the time we try to act it's gone.
696 * We don't need to act on this PACKAGE_CHANGED when this happens;
697 * we'll get a PACKAGE_REMOVED later and clean up then.
698 *
699 * RemoteException can't actually happen; the package manager is
700 * running in this same process.
701 */
702 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700703 break;
704 }
705 }
706 }
707 } else {
708 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
709 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700710 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700711 // If this is an outright uninstall rather than the first half of an
712 // app update sequence, cancel the jobs associated with the app.
713 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
714 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
715 if (DEBUG) {
716 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
717 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700718 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Christopher Tate7060b042014-06-09 19:50:00 -0700719 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700720 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700721 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
722 if (DEBUG) {
723 Slog.d(TAG, "Removing jobs for user: " + userId);
724 }
725 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700726 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
727 // Has this package scheduled any jobs, such that we will take action
728 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700729 if (pkgUid != -1) {
730 List<JobStatus> jobsForUid;
731 synchronized (mLock) {
732 jobsForUid = mJobs.getJobsByUid(pkgUid);
733 }
734 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
735 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
736 if (DEBUG) {
737 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
738 + pkgUid + " has jobs");
739 }
740 setResultCode(Activity.RESULT_OK);
741 break;
742 }
743 }
744 }
745 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
746 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700747 if (pkgUid != -1) {
748 if (DEBUG) {
749 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
750 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700751 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700752 }
Christopher Tate7060b042014-06-09 19:50:00 -0700753 }
754 }
755 };
756
Christopher Tateb5c07882016-05-26 17:11:09 -0700757 private String getPackageName(Intent intent) {
758 Uri uri = intent.getData();
759 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
760 return pkg;
761 }
762
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700763 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700764 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800765 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700766 }
767
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700768 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800769 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700770 }
771
772 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800773 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700774 }
775
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700776 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800777 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700778 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700779
780 @Override public void onUidCachedChanged(int uid, boolean cached) {
781 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700782 };
783
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800784 public Object getLock() {
785 return mLock;
786 }
787
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700788 public JobStore getJobStore() {
789 return mJobs;
790 }
791
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700792 public Constants getConstants() {
793 return mConstants;
794 }
795
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700796 @Override
797 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700798 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
799 // Let's kick any outstanding jobs for this user.
800 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
801 }
802
803 @Override
804 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700805 // Let's kick any outstanding jobs for this user.
806 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
807 }
808
809 @Override
810 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700811 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700812 }
813
Makoto Onuki15407842018-01-19 14:23:11 -0800814 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800815 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800816 */
Makoto Onukie4918212018-02-06 11:30:15 -0800817 private boolean isUidActive(int uid) {
818 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800819 }
820
Makoto Onukie4918212018-02-06 11:30:15 -0800821 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800822
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700823 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
824 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700825 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800826 if (ActivityManager.getService().isAppStartModeDisabled(uId,
827 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700828 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
829 + " -- package not allowed to start");
830 return JobScheduler.RESULT_FAILURE;
831 }
832 } catch (RemoteException e) {
833 }
Christopher Tatea732f012017-10-26 17:26:53 -0700834
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800835 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700836 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
837
838 if (work != null && toCancel != null) {
839 // Fast path: we are adding work to an existing job, and the JobInfo is not
840 // changing. We can just directly enqueue this work in to the job.
841 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -0800842
Dianne Hackborn342e6032017-04-13 18:04:31 -0700843 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -0800844
845 // If any of work item is enqueued when the source is in the foreground,
846 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -0800847 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800848
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700849 return JobScheduler.RESULT_SUCCESS;
850 }
851 }
852
853 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -0800854
855 // Give exemption if the source is in the foreground just now.
856 // Note if it's a sync job, this method is called on the handler so it's not exactly
857 // the state when requestSync() was called, but that should be fine because of the
858 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -0800859 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800860
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700861 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800862 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800863 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800864 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
865 Slog.w(TAG, "Too many jobs for uid " + uId);
866 throw new IllegalStateException("Apps may not schedule more than "
867 + MAX_JOBS_PER_APP + " distinct jobs");
868 }
869 }
870
Dianne Hackborna47223f2017-03-30 13:49:13 -0700871 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700872 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700873
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700874 if (toCancel != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700875 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700876 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700877 if (work != null) {
878 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700879 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700880 }
881 startTrackingJobLocked(jobStatus, toCancel);
Tej Singhd5747a62018-01-08 20:57:35 -0800882 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -0800883 uId, null, jobStatus.getBatteryName(),
Tej Singh33a412b2018-03-16 18:43:59 -0700884 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
885 JobProtoEnums.STOP_REASON_CANCELLED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700886
887 // If the job is immediately ready to run, then we can just immediately
888 // put it in the pending list and try to schedule it. This is especially
889 // important for jobs with a 0 deadline constraint, since they will happen a fair
890 // amount, we want to handle them as quickly as possible, and semantically we want to
891 // make sure we have started holding the wake lock for the job before returning to
892 // the caller.
893 // If the job is not yet ready to run, there is nothing more to do -- we are
894 // now just waiting for one of its controllers to change state and schedule
895 // the job appropriately.
896 if (isReadyToBeExecutedLocked(jobStatus)) {
897 // This is a new job, we can just immediately put it on the pending
898 // list and try to run it.
899 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700900 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700901 maybeRunPendingJobsLocked();
902 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800903 }
Christopher Tate7060b042014-06-09 19:50:00 -0700904 return JobScheduler.RESULT_SUCCESS;
905 }
906
907 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800908 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800909 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
910 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
911 for (int i = jobs.size() - 1; i >= 0; i--) {
912 JobStatus job = jobs.get(i);
913 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700914 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800915 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700916 }
Christopher Tate7060b042014-06-09 19:50:00 -0700917 }
918
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600919 public JobInfo getPendingJob(int uid, int jobId) {
920 synchronized (mLock) {
921 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
922 for (int i = jobs.size() - 1; i >= 0; i--) {
923 JobStatus job = jobs.get(i);
924 if (job.getJobId() == jobId) {
925 return job.getJob();
926 }
927 }
928 return null;
929 }
930 }
931
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700932 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800933 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700934 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
935 for (int i=0; i<jobsForUser.size(); i++) {
936 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700937 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700938 }
Christopher Tate7060b042014-06-09 19:50:00 -0700939 }
940 }
941
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -0700942 private void cancelJobsForNonExistentUsers() {
943 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
944 synchronized (mLock) {
945 mJobs.removeJobsOfNonUsers(umi.getUserIds());
946 }
947 }
948
Makoto Onukie7b96182017-08-30 14:53:16 -0700949 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
950 if ("android".equals(pkgName)) {
951 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
952 return;
953 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700954 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700955 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
956 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
957 final JobStatus job = jobsForUid.get(i);
958 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700959 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700960 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700961 }
962 }
963 }
964
Christopher Tate7060b042014-06-09 19:50:00 -0700965 /**
966 * Entry point from client to cancel all jobs originating from their uid.
967 * This will remove the job from the master list, and cancel the job if it was staged for
968 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700969 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800970 *
Christopher Tate7060b042014-06-09 19:50:00 -0700971 */
Christopher Tate8c67d122017-09-29 16:54:26 -0700972 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -0700973 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700974 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -0700975 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -0700976 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700977
978 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800979 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700980 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
981 for (int i=0; i<jobsForUid.size(); i++) {
982 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700983 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -0700984 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700985 }
Christopher Tate7060b042014-06-09 19:50:00 -0700986 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700987 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -0700988 }
989
990 /**
991 * Entry point from client to cancel the job corresponding to the jobId provided.
992 * This will remove the job from the master list, and cancel the job if it was staged for
993 * execution or being executed.
994 * @param uid Uid of the calling client.
995 * @param jobId Id of the job, provided at schedule-time.
996 */
Makoto Onukid2bfec62018-01-12 13:58:01 -0800997 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -0700998 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800999 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001000 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001001 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001002 cancelJobImplLocked(toCancel, null,
1003 "cancel() called by app, callingUid=" + callingUid
1004 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001005 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001006 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001007 }
1008 }
1009
Dianne Hackborn729a3282017-06-09 16:06:01 -07001010 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001011 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1012 cancelled.unprepareLocked(ActivityManager.getService());
1013 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1014 // Remove from pending queue.
1015 if (mPendingJobs.remove(cancelled)) {
1016 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001017 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001018 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001019 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001020 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001021 }
1022
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001023 void updateUidState(int uid, int procState) {
1024 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001025 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1026 // Only use this if we are exactly the top app. All others can live
1027 // with just the foreground priority. This means that persistent processes
1028 // can never be the top app priority... that is fine.
1029 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001030 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001031 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001032 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001033 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001034 }
1035 }
1036 }
1037
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001038 @Override
1039 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001040 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001041 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001042 // When becoming idle, make sure no jobs are actively running,
1043 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001044 for (int i=0; i<mActiveServices.size(); i++) {
1045 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001046 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001047 if (executing != null
1048 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001049 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1050 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001051 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001052 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001053 } else {
1054 // When coming out of idle, allow thing to start back up.
1055 if (mReadyToRock) {
1056 if (mLocalDeviceIdleController != null) {
1057 if (!mReportedActive) {
1058 mReportedActive = true;
1059 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001060 }
1061 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001062 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001063 }
1064 }
1065 }
1066 }
1067
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001068 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001069 // active is true if pending queue contains jobs OR some job is running.
1070 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001071 if (mPendingJobs.size() <= 0) {
1072 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001073 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001074 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001075 if (job != null
1076 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001077 && !job.dozeWhitelisted
1078 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001079 // We will report active if we have a job running and it is not an exception
1080 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001081 active = true;
1082 break;
1083 }
1084 }
1085 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001086
1087 if (mReportedActive != active) {
1088 mReportedActive = active;
1089 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001090 mLocalDeviceIdleController.setJobsActive(active);
1091 }
1092 }
1093 }
1094
Christopher Tated117b292018-01-05 17:32:36 -08001095 void reportAppUsage(String packageName, int userId) {
1096 // This app just transitioned into interactive use or near equivalent, so we should
1097 // take a look at its job state for feedback purposes.
1098 }
1099
Christopher Tate7060b042014-06-09 19:50:00 -07001100 /**
1101 * Initializes the system service.
1102 * <p>
1103 * Subclasses must define a single argument constructor that accepts the context
1104 * and passes it to super.
1105 * </p>
1106 *
1107 * @param context The system server context.
1108 */
1109 public JobSchedulerService(Context context) {
1110 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001111
1112 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001113 mActivityManagerInternal = Preconditions.checkNotNull(
1114 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001115
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001116 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001117 mConstants = new Constants();
1118 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001119 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001120
1121 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001122 mStandbyTracker = new StandbyTracker();
1123 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1124 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001125
1126 // The job store needs to call back
1127 publishLocalService(JobSchedulerInternal.class, new LocalService());
1128
1129 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001130 mJobs = JobStore.initAndGet(this);
1131
Christopher Tate7060b042014-06-09 19:50:00 -07001132 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001133 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001134 mControllers.add(new ConnectivityController(this));
1135 mControllers.add(new TimeController(this));
1136 mControllers.add(new IdleController(this));
1137 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001138 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001139 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001140 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001141 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001142 mControllers.add(new ContentObserverController(this));
1143 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001144 mControllers.add(mDeviceIdleJobsController);
Christopher Tate616541d2017-07-26 14:27:38 -07001145
1146 // If the job store determined that it can't yet reschedule persisted jobs,
1147 // we need to start watching the clock.
1148 if (!mJobs.jobTimesInflatedValid()) {
1149 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1150 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1151 }
Christopher Tate7060b042014-06-09 19:50:00 -07001152 }
1153
Christopher Tate616541d2017-07-26 14:27:38 -07001154 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1155 @Override
1156 public void onReceive(Context context, Intent intent) {
1157 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1158 // When we reach clock sanity, recalculate the temporal windows
1159 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001160 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001161 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1162
1163 // We've done our job now, so stop watching the time.
1164 context.unregisterReceiver(this);
1165
1166 // And kick off the work to update the affected jobs, using a secondary
1167 // thread instead of chugging away here on the main looper thread.
1168 FgThread.getHandler().post(mJobTimeUpdater);
1169 }
1170 }
1171 }
1172 };
1173
1174 private final Runnable mJobTimeUpdater = () -> {
1175 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1176 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1177 synchronized (mLock) {
1178 // Note: we intentionally both look up the existing affected jobs and replace them
1179 // with recalculated ones inside the same lock lifetime.
1180 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1181
1182 // Now, at each position [i], we have both the existing JobStatus
1183 // and the one that replaces it.
1184 final int N = toAdd.size();
1185 for (int i = 0; i < N; i++) {
1186 final JobStatus oldJob = toRemove.get(i);
1187 final JobStatus newJob = toAdd.get(i);
1188 if (DEBUG) {
1189 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1190 }
1191 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1192 }
1193 }
1194 };
1195
Christopher Tate7060b042014-06-09 19:50:00 -07001196 @Override
1197 public void onStart() {
1198 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1199 }
1200
1201 @Override
1202 public void onBootPhase(int phase) {
1203 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001204 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001205
Makoto Onukie4918212018-02-06 11:30:15 -08001206 mAppStateTracker = Preconditions.checkNotNull(
1207 LocalServices.getService(AppStateTracker.class));
Christopher Tated1aebb32018-01-31 13:24:14 -08001208 setNextHeartbeatAlarm();
Makoto Onuki15407842018-01-19 14:23:11 -08001209
Shreyas Basarge5db09082016-01-07 13:38:29 +00001210 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001211 final IntentFilter filter = new IntentFilter();
1212 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1213 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001214 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1215 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001216 filter.addDataScheme("package");
1217 getContext().registerReceiverAsUser(
1218 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1219 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1220 getContext().registerReceiverAsUser(
1221 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001222 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001223 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001224 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001225 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1226 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001227 } catch (RemoteException e) {
1228 // ignored; both services live in system_server
1229 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001230 // Remove any jobs that are not associated with any of the current users.
1231 cancelJobsForNonExistentUsers();
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001232 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001233 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001234 // Let's go!
1235 mReadyToRock = true;
1236 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1237 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001238 mLocalDeviceIdleController
1239 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001240 // Create the "runners".
1241 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1242 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001243 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001244 getContext().getMainLooper()));
1245 }
1246 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001247 mJobs.forEachJob((job) -> {
1248 for (int controller = 0; controller < mControllers.size(); controller++) {
1249 final StateController sc = mControllers.get(controller);
1250 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001251 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001252 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001253 // GO GO GO!
1254 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1255 }
Christopher Tate7060b042014-06-09 19:50:00 -07001256 }
1257 }
1258
1259 /**
1260 * Called when we have a job status object that we need to insert in our
1261 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1262 * about.
1263 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001264 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1265 if (!jobStatus.isPreparedLocked()) {
1266 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1267 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001268 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001269 final boolean update = mJobs.add(jobStatus);
1270 if (mReadyToRock) {
1271 for (int i = 0; i < mControllers.size(); i++) {
1272 StateController controller = mControllers.get(i);
1273 if (update) {
1274 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001275 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001276 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001277 }
Christopher Tate7060b042014-06-09 19:50:00 -07001278 }
1279 }
1280
1281 /**
1282 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1283 * object removed.
1284 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001285 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001286 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001287 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001288 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001289
1290 // Remove from store as well as controllers.
1291 final boolean removed = mJobs.remove(jobStatus, writeBack);
1292 if (removed && mReadyToRock) {
1293 for (int i=0; i<mControllers.size(); i++) {
1294 StateController controller = mControllers.get(i);
1295 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001296 }
1297 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001298 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001299 }
1300
Dianne Hackborn729a3282017-06-09 16:06:01 -07001301 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001302 for (int i=0; i<mActiveServices.size(); i++) {
1303 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001304 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001305 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001306 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001307 return true;
1308 }
1309 }
1310 return false;
1311 }
1312
1313 /**
1314 * @param job JobStatus we are querying against.
1315 * @return Whether or not the job represented by the status object is currently being run or
1316 * is pending.
1317 */
1318 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001319 for (int i=0; i<mActiveServices.size(); i++) {
1320 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001321 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001322 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1323 return true;
1324 }
1325 }
1326 return false;
1327 }
1328
Dianne Hackborn807de782016-04-07 17:54:41 -07001329 void noteJobsPending(List<JobStatus> jobs) {
1330 for (int i = jobs.size() - 1; i >= 0; i--) {
1331 JobStatus job = jobs.get(i);
1332 mJobPackageTracker.notePending(job);
1333 }
1334 }
1335
1336 void noteJobsNonpending(List<JobStatus> jobs) {
1337 for (int i = jobs.size() - 1; i >= 0; i--) {
1338 JobStatus job = jobs.get(i);
1339 mJobPackageTracker.noteNonpending(job);
1340 }
1341 }
1342
Christopher Tate7060b042014-06-09 19:50:00 -07001343 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001344 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1345 * specify an override deadline on a failed job (the failed job will run even though it's not
1346 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1347 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1348 *
Christopher Tate7060b042014-06-09 19:50:00 -07001349 * @param failureToReschedule Provided job status that we will reschedule.
1350 * @return A newly instantiated JobStatus with the same constraints as the last job except
1351 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001352 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001353 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001354 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001355 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001356 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001357 final JobInfo job = failureToReschedule.getJob();
1358
1359 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001360 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1361 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001362
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001363 if (failureToReschedule.hasWorkLocked()) {
1364 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1365 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1366 + backoffAttempts + " > work limit "
1367 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1368 return null;
1369 }
1370 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1371 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1372 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1373 return null;
1374 }
1375
Christopher Tate7060b042014-06-09 19:50:00 -07001376 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001377 case JobInfo.BACKOFF_POLICY_LINEAR: {
1378 long backoff = initialBackoffMillis;
1379 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1380 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1381 }
1382 delayMillis = backoff * backoffAttempts;
1383 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001384 default:
1385 if (DEBUG) {
1386 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1387 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001388 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1389 long backoff = initialBackoffMillis;
1390 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1391 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1392 }
1393 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1394 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001395 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001396 delayMillis =
1397 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001398 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1399 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001400 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001401 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001402 for (int ic=0; ic<mControllers.size(); ic++) {
1403 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001404 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001405 }
1406 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001407 }
1408
1409 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001410 * Called after a periodic has executed so we can reschedule it. We take the last execution
1411 * time of the job to be the time of completion (i.e. the time at which this function is
1412 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001413 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001414 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1415 * to underscheduling at least, rather than if we had taken the last execution time to be the
1416 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001417 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1418 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001419 * @return A new job representing the execution criteria for this instantiation of the
1420 * recurring job.
1421 */
1422 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001423 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001424 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001425 long runEarly = 0L;
1426
1427 // If this periodic was rescheduled it won't have a deadline.
1428 if (periodicToReschedule.hasDeadlineConstraint()) {
1429 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1430 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001431 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001432 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001433 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1434 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001435
1436 if (DEBUG) {
1437 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1438 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1439 }
Christopher Tatea732f012017-10-26 17:26:53 -07001440 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1441 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1442 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001443 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001444 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001445 }
1446
Christopher Tate325768c2018-03-07 16:07:56 -08001447 /*
1448 * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1449 * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1450 * reboot behavior.
1451 */
Christopher Tated1aebb32018-01-31 13:24:14 -08001452 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
Christopher Tate325768c2018-03-07 16:07:56 -08001453 // The furthest back in pre-boot time that we need to bother with
1454 long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1455 boolean cacheHit = false;
Christopher Tated1aebb32018-01-31 13:24:14 -08001456 synchronized (mLock) {
Christopher Tate325768c2018-03-07 16:07:56 -08001457 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1458 if (jobPackages != null) {
1459 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1460 if (cachedValue < Long.MAX_VALUE) {
1461 cacheHit = true;
1462 heartbeat = cachedValue;
1463 }
1464 }
1465 if (!cacheHit) {
1466 // We haven't seen it yet; ask usage stats about it
1467 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1468 if (timeSinceJob < Long.MAX_VALUE) {
1469 // Usage stats knows about it from before, so calculate back from that
1470 // and go from there.
1471 heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1472 }
1473 // If usage stats returned its "not found" MAX_VALUE, we still have the
1474 // negative default 'heartbeat' value we established above
1475 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1476 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001477 }
1478 if (DEBUG_STANDBY) {
Christopher Tate325768c2018-03-07 16:07:56 -08001479 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1480 + packageName + "/" + userId);
Christopher Tated1aebb32018-01-31 13:24:14 -08001481 }
1482 return heartbeat;
1483 }
1484
1485 long heartbeatWhenJobsLastRun(JobStatus job) {
1486 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1487 }
1488
Christopher Tate325768c2018-03-07 16:07:56 -08001489 void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1490 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1491 if (jobPackages == null) {
1492 jobPackages = new HashMap<>();
1493 mLastJobHeartbeats.put(userId, jobPackages);
1494 }
1495 jobPackages.put(packageName, heartbeat);
1496 }
1497
Christopher Tate7060b042014-06-09 19:50:00 -07001498 // JobCompletedListener implementations.
1499
1500 /**
1501 * A job just finished executing. We fetch the
1502 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001503 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001504 * @param jobStatus Completed job.
1505 * @param needsReschedule Whether the implementing class should reschedule this job.
1506 */
1507 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001508 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001509 if (DEBUG) {
1510 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1511 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001512
1513 // If the job wants to be rescheduled, we first need to make the next upcoming
1514 // job so we can transfer any appropriate state over from the previous job when
1515 // we stop it.
1516 final JobStatus rescheduledJob = needsReschedule
1517 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1518
Shreyas Basarge73f10252016-02-11 17:06:13 +00001519 // Do not write back immediately if this is a periodic job. The job may get lost if system
1520 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001521 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001522 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001523 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001524 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001525 // We still want to check for jobs to execute, because this job may have
1526 // scheduled a new job under the same job id, and now we can run it.
1527 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001528 return;
1529 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001530
1531 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001532 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001533 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001534 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001535 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001536 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001537 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001538 } else if (jobStatus.getJob().isPeriodic()) {
1539 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001540 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001541 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001542 } catch (SecurityException e) {
1543 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1544 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001545 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001546 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001547 jobStatus.unprepareLocked(ActivityManager.getService());
1548 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001549 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001550 }
1551
1552 // StateChangedListener implementations.
1553
1554 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001555 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1556 * some controller's state has changed, so as to run through the list of jobs and start/stop
1557 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001558 */
1559 @Override
1560 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001561 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001562 }
1563
1564 @Override
1565 public void onRunJobNow(JobStatus jobStatus) {
1566 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1567 }
1568
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001569 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001570
1571 public JobHandler(Looper looper) {
1572 super(looper);
1573 }
1574
1575 @Override
1576 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001577 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001578 if (!mReadyToRock) {
1579 return;
1580 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001581 switch (message.what) {
1582 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001583 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001584 // runNow can be null, which is a controller's way of indicating that its
1585 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001586 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001587 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001588 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001589 } else {
1590 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001591 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001592 } break;
1593 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001594 if (mReportedActive) {
1595 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001596 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001597 } else {
1598 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001599 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001600 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001601 break;
1602 case MSG_CHECK_JOB_GREEDY:
1603 queueReadyJobsForExecutionLocked();
1604 break;
1605 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001606 cancelJobImplLocked((JobStatus) message.obj, null,
1607 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001608 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001609
1610 case MSG_UID_STATE_CHANGED: {
1611 final int uid = message.arg1;
1612 final int procState = message.arg2;
1613 updateUidState(uid, procState);
1614 break;
1615 }
1616 case MSG_UID_GONE: {
1617 final int uid = message.arg1;
1618 final boolean disabled = message.arg2 != 0;
1619 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1620 if (disabled) {
1621 cancelJobsForUid(uid, "uid gone");
1622 }
1623 synchronized (mLock) {
1624 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1625 }
1626 break;
1627 }
1628 case MSG_UID_ACTIVE: {
1629 final int uid = message.arg1;
1630 synchronized (mLock) {
1631 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1632 }
1633 break;
1634 }
1635 case MSG_UID_IDLE: {
1636 final int uid = message.arg1;
1637 final boolean disabled = message.arg2 != 0;
1638 if (disabled) {
1639 cancelJobsForUid(uid, "app uid idle");
1640 }
1641 synchronized (mLock) {
1642 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1643 }
1644 break;
1645 }
1646
Matthew Williams75fc5252014-09-02 16:17:53 -07001647 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001648 maybeRunPendingJobsLocked();
1649 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1650 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001651 }
1652 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001653 }
Christopher Tate7060b042014-06-09 19:50:00 -07001654
Dianne Hackborn6d068262017-05-16 13:14:37 -07001655 private void stopNonReadyActiveJobsLocked() {
1656 for (int i=0; i<mActiveServices.size(); i++) {
1657 JobServiceContext serviceContext = mActiveServices.get(i);
1658 final JobStatus running = serviceContext.getRunningJobLocked();
1659 if (running != null && !running.isReady()) {
1660 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001661 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1662 "cancelled due to unsatisfied constraints");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001663 }
1664 }
1665 }
1666
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001667 /**
1668 * Run through list of jobs and execute all possible - at least one is expired so we do
1669 * as many as we can.
1670 */
1671 private void queueReadyJobsForExecutionLocked() {
1672 if (DEBUG) {
1673 Slog.d(TAG, "queuing all ready jobs for execution:");
1674 }
1675 noteJobsNonpending(mPendingJobs);
1676 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001677 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001678 mJobs.forEachJob(mReadyQueueFunctor);
1679 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001680
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001681 if (DEBUG) {
1682 final int queuedJobs = mPendingJobs.size();
1683 if (queuedJobs == 0) {
1684 Slog.d(TAG, "No jobs pending.");
1685 } else {
1686 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001687 }
1688 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001689 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001690
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001691 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001692 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001693
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001694 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001695 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001696 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001697 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001698 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001699 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001700 if (newReadyJobs == null) {
1701 newReadyJobs = new ArrayList<JobStatus>();
1702 }
1703 newReadyJobs.add(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001704 }
1705 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001706
1707 public void postProcess() {
1708 if (newReadyJobs != null) {
1709 noteJobsPending(newReadyJobs);
1710 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001711 if (mPendingJobs.size() > 1) {
1712 mPendingJobs.sort(mEnqueueTimeComparator);
1713 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001714 }
1715 newReadyJobs = null;
1716 }
1717 }
1718 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1719
1720 /**
1721 * The state of at least one job has changed. Here is where we could enforce various
1722 * policies on when we want to execute jobs.
1723 * Right now the policy is such:
1724 * If >1 of the ready jobs is idle mode we send all of them off
1725 * if more than 2 network connectivity jobs are ready we send them all off.
1726 * If more than 4 jobs total are ready we send them all off.
1727 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1728 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001729 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001730 int chargingCount;
1731 int batteryNotLowCount;
1732 int storageNotLowCount;
1733 int idleCount;
1734 int backoffCount;
1735 int connectivityCount;
1736 int contentCount;
1737 List<JobStatus> runnableJobs;
1738
1739 public MaybeReadyJobQueueFunctor() {
1740 reset();
1741 }
1742
1743 // Functor method invoked for each job via JobStore.forEachJob()
1744 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001745 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001746 if (isReadyToBeExecutedLocked(job)) {
1747 try {
1748 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1749 job.getJob().getService().getPackageName())) {
1750 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1751 + job.getJob().toString() + " -- package not allowed to start");
1752 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1753 return;
1754 }
1755 } catch (RemoteException e) {
1756 }
1757 if (job.getNumFailures() > 0) {
1758 backoffCount++;
1759 }
1760 if (job.hasIdleConstraint()) {
1761 idleCount++;
1762 }
1763 if (job.hasConnectivityConstraint()) {
1764 connectivityCount++;
1765 }
1766 if (job.hasChargingConstraint()) {
1767 chargingCount++;
1768 }
1769 if (job.hasBatteryNotLowConstraint()) {
1770 batteryNotLowCount++;
1771 }
1772 if (job.hasStorageNotLowConstraint()) {
1773 storageNotLowCount++;
1774 }
1775 if (job.hasContentTriggerConstraint()) {
1776 contentCount++;
1777 }
1778 if (runnableJobs == null) {
1779 runnableJobs = new ArrayList<>();
1780 }
1781 runnableJobs.add(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001782 }
1783 }
1784
1785 public void postProcess() {
1786 if (backoffCount > 0 ||
1787 idleCount >= mConstants.MIN_IDLE_COUNT ||
1788 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1789 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1790 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1791 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1792 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1793 (runnableJobs != null
1794 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1795 if (DEBUG) {
1796 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1797 }
1798 noteJobsPending(runnableJobs);
1799 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001800 if (mPendingJobs.size() > 1) {
1801 mPendingJobs.sort(mEnqueueTimeComparator);
1802 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001803 } else {
1804 if (DEBUG) {
1805 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1806 }
1807 }
1808
1809 // Be ready for next time
1810 reset();
1811 }
1812
1813 private void reset() {
1814 chargingCount = 0;
1815 idleCount = 0;
1816 backoffCount = 0;
1817 connectivityCount = 0;
1818 batteryNotLowCount = 0;
1819 storageNotLowCount = 0;
1820 contentCount = 0;
1821 runnableJobs = null;
1822 }
1823 }
1824 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1825
1826 private void maybeQueueReadyJobsForExecutionLocked() {
1827 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1828
1829 noteJobsNonpending(mPendingJobs);
1830 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001831 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001832 mJobs.forEachJob(mMaybeQueueFunctor);
1833 mMaybeQueueFunctor.postProcess();
1834 }
1835
Christopher Tated1aebb32018-01-31 13:24:14 -08001836 /**
1837 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
1838 */
1839 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1840
1841 @Override
1842 public void onAlarm() {
1843 synchronized (mLock) {
1844 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1845 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1846 if (beatsElapsed > 0) {
1847 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1848 advanceHeartbeatLocked(beatsElapsed);
1849 }
Christopher Tatea732f012017-10-26 17:26:53 -07001850 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001851 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07001852 }
Christopher Tatea732f012017-10-26 17:26:53 -07001853 }
1854
Christopher Tated1aebb32018-01-31 13:24:14 -08001855 // Intentionally does not touch the alarm timing
1856 void advanceHeartbeatLocked(long beatsElapsed) {
1857 mHeartbeat += beatsElapsed;
1858 if (DEBUG_STANDBY) {
1859 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1860 + " to " + mHeartbeat);
1861 }
1862 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
1863 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1864 // new jobs scheduled by apps in that bucket will be permitted to run
1865 // immediately.
1866 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07001867 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001868 // Did we reach or cross a bucket boundary?
1869 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1870 didAdvanceBucket = true;
1871 }
1872 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07001873 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1874 }
1875 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001876 Slog.v(TAG, " Bucket " + i + " next heartbeat "
1877 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07001878 }
1879 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001880
1881 if (didAdvanceBucket) {
1882 if (DEBUG_STANDBY) {
1883 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1884 }
1885 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1886 }
1887 }
1888
1889 void setNextHeartbeatAlarm() {
1890 final long heartbeatLength;
1891 synchronized (mLock) {
1892 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1893 }
1894 final long now = sElapsedRealtimeClock.millis();
1895 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1896 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1897 if (DEBUG_STANDBY) {
1898 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1899 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1900 }
1901 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1902 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1903 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07001904 }
1905
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001906 /**
1907 * Criteria for moving a job into the pending queue:
1908 * - It's ready.
1909 * - It's not pending.
1910 * - It's not already running on a JSC.
1911 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07001912 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001913 * - The component is enabled and runnable.
1914 */
1915 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001916 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001917
1918 if (DEBUG) {
1919 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1920 + " ready=" + jobReady);
1921 }
1922
1923 // This is a condition that is very likely to be false (most jobs that are
1924 // scheduled are sitting there, not ready yet) and very cheap to check (just
1925 // a few conditions on data in JobStatus).
1926 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08001927 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
1928 Slog.v(TAG, " NOT READY: " + job);
1929 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001930 return false;
1931 }
1932
1933 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001934
1935 final int userId = job.getUserId();
1936 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1937
1938 if (DEBUG) {
1939 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001940 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001941 }
1942
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001943 // These are also fairly cheap to check, though they typically will not
1944 // be conditions we fail.
1945 if (!jobExists || !userStarted) {
1946 return false;
1947 }
1948
1949 final boolean jobPending = mPendingJobs.contains(job);
1950 final boolean jobActive = isCurrentlyActiveLocked(job);
1951
1952 if (DEBUG) {
1953 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1954 + " pending=" + jobPending + " active=" + jobActive);
1955 }
1956
1957 // These can be a little more expensive (especially jobActive, since we need to
1958 // go through the array of all potentially active jobs), so we are doing them
1959 // later... but still before checking with the package manager!
1960 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001961 return false;
1962 }
1963
Christopher Tatea732f012017-10-26 17:26:53 -07001964 // If the app is in a non-active standby bucket, make sure we've waited
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001965 // an appropriate amount of time since the last invocation. During device-
1966 // wide parole, standby bucketing is ignored.
Makoto Onuki959acb52018-01-26 14:10:03 -08001967 //
Christopher Tate20afddd2018-02-28 15:19:19 -08001968 // Jobs in 'active' apps are not subject to standby, nor are jobs that are
1969 // specifically marked as exempt.
1970 if (DEBUG_STANDBY) {
1971 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1972 + " parole=" + mInParole + " active=" + job.uidActive
1973 + " exempt=" + job.getJob().isExemptedFromAppStandby());
1974 }
1975 if (!mInParole
1976 && !job.uidActive
1977 && !job.getJob().isExemptedFromAppStandby()) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001978 final int bucket = job.getStandbyBucket();
Christopher Tate20afddd2018-02-28 15:19:19 -08001979 if (DEBUG_STANDBY) {
1980 Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
1981 + " next=" + mNextBucketHeartbeat[bucket]);
1982 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001983 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001984 // Only skip this job if the app is still waiting for the end of its nominal
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001985 // bucket interval. Once it's waited that long, we let it go ahead and clear.
1986 // The final (NEVER) bucket is special; we never age those apps' jobs into
1987 // runnability.
Christopher Tated1aebb32018-01-31 13:24:14 -08001988 final long appLastRan = heartbeatWhenJobsLastRun(job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001989 if (bucket >= mConstants.STANDBY_BEATS.length
Christopher Tated1aebb32018-01-31 13:24:14 -08001990 || (mHeartbeat > appLastRan
1991 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001992 // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1993 if (job.getWhenStandbyDeferred() == 0) {
1994 if (DEBUG_STANDBY) {
1995 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
Christopher Tated1aebb32018-01-31 13:24:14 -08001996 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
1997 + " for " + job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001998 }
1999 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tate0c4d7682017-12-06 15:10:22 -08002000 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002001 return false;
2002 } else {
2003 if (DEBUG_STANDBY) {
2004 Slog.v(TAG, "Bucket deferred job aged into runnability at "
2005 + mHeartbeat + " : " + job);
2006 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08002007 }
Christopher Tatea732f012017-10-26 17:26:53 -07002008 }
Christopher Tatea732f012017-10-26 17:26:53 -07002009 }
2010
2011 // The expensive check last: validate that the defined package+service is
2012 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002013 final boolean componentPresent;
2014 try {
2015 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2016 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2017 userId) != null);
2018 } catch (RemoteException e) {
2019 throw e.rethrowAsRuntimeException();
2020 }
2021
2022 if (DEBUG) {
2023 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2024 + " componentPresent=" + componentPresent);
2025 }
2026
2027 // Everything else checked out so far, so this is the final yes/no check
2028 return componentPresent;
2029 }
2030
2031 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002032 * Reconcile jobs in the pending queue against available execution contexts.
2033 * A controller can force a job into the pending queue even if it's already running, but
2034 * here is where we decide whether to actually execute it.
2035 */
2036 private void maybeRunPendingJobsLocked() {
2037 if (DEBUG) {
2038 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2039 }
2040 assignJobsToContextsLocked();
2041 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002042 }
2043
Dianne Hackborn807de782016-04-07 17:54:41 -07002044 private int adjustJobPriority(int curPriority, JobStatus job) {
2045 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2046 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002047 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002048 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002049 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002050 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2051 }
2052 }
2053 return curPriority;
2054 }
2055
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002056 private int evaluateJobPriorityLocked(JobStatus job) {
2057 int priority = job.getPriority();
2058 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002059 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002060 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002061 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2062 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002063 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002064 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002065 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002066 }
2067
Christopher Tate7060b042014-06-09 19:50:00 -07002068 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00002069 * Takes jobs from pending queue and runs them on available contexts.
2070 * If no contexts are available, preempts lower priority jobs to
2071 * run higher priority ones.
2072 * Lock on mJobs before calling this function.
2073 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08002074 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002075 if (DEBUG) {
2076 Slog.d(TAG, printPendingQueue());
2077 }
2078
Dianne Hackborn970510b2016-02-24 16:56:42 -08002079 int memLevel;
2080 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08002081 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002082 } catch (RemoteException e) {
2083 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
2084 }
2085 switch (memLevel) {
2086 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002087 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002088 break;
2089 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002090 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002091 break;
2092 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002093 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002094 break;
2095 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002096 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002097 break;
2098 }
2099
2100 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
2101 boolean[] act = mTmpAssignAct;
2102 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2103 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002104 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002105 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2106 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002107 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002108 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002109 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002110 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2111 numForeground++;
2112 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002113 }
2114 act[i] = false;
2115 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002116 }
2117 if (DEBUG) {
2118 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2119 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002120 for (int i=0; i<mPendingJobs.size(); i++) {
2121 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002122
2123 // If job is already running, go to next job.
2124 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2125 if (jobRunningContext != -1) {
2126 continue;
2127 }
2128
Dianne Hackborn970510b2016-02-24 16:56:42 -08002129 final int priority = evaluateJobPriorityLocked(nextPending);
2130 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002131
Shreyas Basarge5db09082016-01-07 13:38:29 +00002132 // Find a context for nextPending. The context should be available OR
2133 // it should have lowest priority among all running jobs
2134 // (sharing the same Uid as nextPending)
2135 int minPriority = Integer.MAX_VALUE;
2136 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002137 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2138 JobStatus job = contextIdToJobMap[j];
2139 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00002140 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002141 if ((numActive < mMaxActiveJobs ||
2142 (priority >= JobInfo.PRIORITY_TOP_APP &&
2143 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08002144 (preferredUid == nextPending.getUid() ||
2145 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2146 // This slot is free, and we haven't yet hit the limit on
2147 // concurrent jobs... we can just throw the job in to here.
2148 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002149 break;
2150 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00002151 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08002152 // the context has a preferred Uid or we have reached the limit on
2153 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00002154 continue;
2155 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002156 if (job.getUid() != nextPending.getUid()) {
2157 continue;
2158 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002159 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002160 continue;
2161 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002162 if (minPriority > nextPending.lastEvaluatedPriority) {
2163 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002164 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00002165 }
2166 }
2167 if (minPriorityContextId != -1) {
2168 contextIdToJobMap[minPriorityContextId] = nextPending;
2169 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002170 numActive++;
2171 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2172 numForeground++;
2173 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002174 }
2175 }
2176 if (DEBUG) {
2177 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2178 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002179 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002180 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002181 boolean preservePreferredUid = false;
2182 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002183 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002184 if (js != null) {
2185 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002186 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00002187 }
2188 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07002189 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002190 preservePreferredUid = true;
2191 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002192 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00002193 if (DEBUG) {
2194 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002195 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002196 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002197 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002198 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002199 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002200 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2201 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002202 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002203 if (mPendingJobs.remove(pendingJob)) {
2204 mJobPackageTracker.noteNonpending(pendingJob);
2205 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002206 }
2207 }
2208 if (!preservePreferredUid) {
2209 mActiveServices.get(i).clearPreferredUid();
2210 }
2211 }
2212 }
2213
2214 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2215 for (int i=0; i<map.length; i++) {
2216 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2217 return i;
2218 }
2219 }
2220 return -1;
2221 }
2222
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002223 final class LocalService implements JobSchedulerInternal {
2224
2225 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002226 * The current bucket heartbeat ordinal
2227 */
2228 public long currentHeartbeat() {
2229 return getCurrentHeartbeat();
2230 }
2231
2232 /**
2233 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2234 */
2235 public long nextHeartbeatForBucket(int bucket) {
2236 synchronized (mLock) {
2237 return mNextBucketHeartbeat[bucket];
2238 }
2239 }
2240
2241 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002242 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2243 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2244 * jobs in a long time is immediately runnable even if the app is bucketed into
2245 * an infrequent time allocation.
2246 */
2247 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2248 final int appStandbyBucket) {
2249 if (appStandbyBucket == 0 ||
2250 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2251 // ACTIVE => everything can be run right away
2252 // NEVER => we won't run them anyway, so let them go in the future
2253 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002254 if (DEBUG_STANDBY) {
2255 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2256 + packageName + "/" + userId);
2257 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002258 return 0;
2259 }
2260
Christopher Tated1aebb32018-01-31 13:24:14 -08002261 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2262 if (DEBUG_STANDBY) {
2263 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2264 + packageName + "/" + userId);
2265 }
2266 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002267 }
2268
Christopher Tate325768c2018-03-07 16:07:56 -08002269 public void noteJobStart(String packageName, int userId) {
2270 synchronized (mLock) {
2271 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2272 }
2273 }
2274
Christopher Tate435c2f42018-01-18 12:59:15 -08002275 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002276 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2277 * jobs are always considered pending.
2278 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002279 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002280 public List<JobInfo> getSystemScheduledPendingJobs() {
2281 synchronized (mLock) {
2282 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002283 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2284 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2285 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002286 }
2287 });
2288 return pendingJobs;
2289 }
2290 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002291
2292 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002293 public void cancelJobsForUid(int uid, String reason) {
2294 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2295 }
2296
2297 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002298 public void addBackingUpUid(int uid) {
2299 synchronized (mLock) {
2300 // No need to actually do anything here, since for a full backup the
2301 // activity manager will kill the process which will kill the job (and
2302 // cause it to restart, but now it can't run).
2303 mBackingUpUids.put(uid, uid);
2304 }
2305 }
2306
2307 @Override
2308 public void removeBackingUpUid(int uid) {
2309 synchronized (mLock) {
2310 mBackingUpUids.delete(uid);
2311 // If there are any jobs for this uid, we need to rebuild the pending list
2312 // in case they are now ready to run.
2313 if (mJobs.countJobsForUid(uid) > 0) {
2314 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2315 }
2316 }
2317 }
2318
2319 @Override
2320 public void clearAllBackingUpUids() {
2321 synchronized (mLock) {
2322 if (mBackingUpUids.size() > 0) {
2323 mBackingUpUids.clear();
2324 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2325 }
2326 }
2327 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002328
2329 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002330 public void reportAppUsage(String packageName, int userId) {
2331 JobSchedulerService.this.reportAppUsage(packageName, userId);
2332 }
2333
2334 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002335 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002336 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002337 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002338 }
2339 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002340 }
2341
Shreyas Basarge5db09082016-01-07 13:38:29 +00002342 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002343 * Tracking of app assignments to standby buckets
2344 */
2345 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002346
Christopher Tatea732f012017-10-26 17:26:53 -07002347 // AppIdleStateChangeListener interface for live updates
2348
2349 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002350 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002351 boolean idle, int bucket, int reason) {
Christopher Tatea732f012017-10-26 17:26:53 -07002352 final int uid = mLocalPM.getPackageUid(packageName,
2353 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2354 if (uid < 0) {
2355 if (DEBUG_STANDBY) {
2356 Slog.i(TAG, "App idle state change for unknown app "
2357 + packageName + "/" + userId);
2358 }
2359 return;
2360 }
2361
2362 final int bucketIndex = standbyBucketToBucketIndex(bucket);
2363 // update job bookkeeping out of band
2364 BackgroundThread.getHandler().post(() -> {
2365 if (DEBUG_STANDBY) {
2366 Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2367 }
2368 synchronized (mLock) {
Christopher Tate93defea2018-02-20 15:57:17 -08002369 mJobs.forEachJobForSourceUid(uid, job -> {
2370 // double-check uid vs package name to disambiguate shared uids
2371 if (packageName.equals(job.getSourcePackageName())) {
2372 job.setStandbyBucket(bucketIndex);
2373 }
2374 });
Christopher Tatea732f012017-10-26 17:26:53 -07002375 onControllerStateChanged();
2376 }
2377 });
2378 }
2379
2380 @Override
2381 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002382 if (DEBUG_STANDBY) {
2383 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2384 }
2385 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002386 }
Christopher Tated117b292018-01-05 17:32:36 -08002387
2388 @Override
2389 public void onUserInteractionStarted(String packageName, int userId) {
2390 final int uid = mLocalPM.getPackageUid(packageName,
2391 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2392 if (uid < 0) {
2393 // Quietly ignore; the case is already logged elsewhere
2394 return;
2395 }
2396
Amith Yamasani977e11f2018-02-16 11:29:54 -08002397 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2398 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2399 // Too long ago, not worth logging
2400 sinceLast = 0L;
2401 }
Christopher Tated117b292018-01-05 17:32:36 -08002402 final DeferredJobCounter counter = new DeferredJobCounter();
2403 synchronized (mLock) {
2404 mJobs.forEachJobForSourceUid(uid, counter);
2405 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002406 if (counter.numDeferred() > 0 || sinceLast > 0) {
2407 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2408 (BatteryStatsInternal.class);
2409 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2410 }
Christopher Tated117b292018-01-05 17:32:36 -08002411 }
2412 }
2413
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002414 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002415 private int mDeferred = 0;
2416
2417 public int numDeferred() {
2418 return mDeferred;
2419 }
2420
2421 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002422 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002423 if (job.getWhenStandbyDeferred() > 0) {
2424 mDeferred++;
2425 }
2426 }
Christopher Tatea732f012017-10-26 17:26:53 -07002427 }
2428
2429 public static int standbyBucketToBucketIndex(int bucket) {
2430 // Normalize AppStandby constants to indices into our bookkeeping
Amith Yamasaniafbccb72017-11-27 10:44:24 -08002431 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
2432 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
2433 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
2434 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
Christopher Tatea732f012017-10-26 17:26:53 -07002435 else return 0;
2436 }
2437
Christopher Tated1aebb32018-01-31 13:24:14 -08002438 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002439 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2440 UsageStatsManagerInternal usageStats = LocalServices.getService(
2441 UsageStatsManagerInternal.class);
2442 int bucket = usageStats != null
2443 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2444 : 0;
2445
2446 bucket = standbyBucketToBucketIndex(bucket);
2447
2448 if (DEBUG_STANDBY) {
2449 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2450 }
2451 return bucket;
2452 }
2453
2454 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002455 * Binder stub trampoline implementation
2456 */
2457 final class JobSchedulerStub extends IJobScheduler.Stub {
2458 /** Cache determination of whether a given app can persist jobs
2459 * key is uid of the calling app; value is undetermined/true/false
2460 */
2461 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2462
2463 // Enforce that only the app itself (or shared uid participant) can schedule a
2464 // job that runs one of the app's services, as well as verifying that the
2465 // named service properly requires the BIND_JOB_SERVICE permission
2466 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002467 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002468 final ComponentName service = job.getService();
2469 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002470 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002471 PackageManager.MATCH_DIRECT_BOOT_AWARE
2472 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002473 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002474 if (si == null) {
2475 throw new IllegalArgumentException("No such service " + service);
2476 }
Christopher Tate7060b042014-06-09 19:50:00 -07002477 if (si.applicationInfo.uid != uid) {
2478 throw new IllegalArgumentException("uid " + uid +
2479 " cannot schedule job in " + service.getPackageName());
2480 }
2481 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2482 throw new IllegalArgumentException("Scheduled service " + service
2483 + " does not require android.permission.BIND_JOB_SERVICE permission");
2484 }
Christopher Tate5568f542014-06-18 13:53:31 -07002485 } catch (RemoteException e) {
2486 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002487 }
2488 }
2489
2490 private boolean canPersistJobs(int pid, int uid) {
2491 // If we get this far we're good to go; all we need to do now is check
2492 // whether the app is allowed to persist its scheduled work.
2493 final boolean canPersist;
2494 synchronized (mPersistCache) {
2495 Boolean cached = mPersistCache.get(uid);
2496 if (cached != null) {
2497 canPersist = cached.booleanValue();
2498 } else {
2499 // Persisting jobs is tantamount to running at boot, so we permit
2500 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2501 // permission
2502 int result = getContext().checkPermission(
2503 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2504 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2505 mPersistCache.put(uid, canPersist);
2506 }
2507 }
2508 return canPersist;
2509 }
2510
Makoto Onuki959acb52018-01-26 14:10:03 -08002511 private void validateJobFlags(JobInfo job, int callingUid) {
2512 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2513 getContext().enforceCallingOrSelfPermission(
2514 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2515 }
2516 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2517 if (callingUid != Process.SYSTEM_UID) {
2518 throw new SecurityException("Job has invalid flags");
2519 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002520 if (job.isPeriodic()) {
2521 Slog.wtf(TAG, "Periodic jobs mustn't have"
2522 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002523 }
2524 }
2525 }
2526
Christopher Tate7060b042014-06-09 19:50:00 -07002527 // IJobScheduler implementation
2528 @Override
2529 public int schedule(JobInfo job) throws RemoteException {
2530 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002531 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002532 }
2533 final int pid = Binder.getCallingPid();
2534 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002535 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002536
2537 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002538 if (job.isPersisted()) {
2539 if (!canPersistJobs(pid, uid)) {
2540 throw new IllegalArgumentException("Error: requested job be persisted without"
2541 + " holding RECEIVE_BOOT_COMPLETED permission.");
2542 }
2543 }
Christopher Tate7060b042014-06-09 19:50:00 -07002544
Makoto Onuki959acb52018-01-26 14:10:03 -08002545 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002546
Christopher Tate7060b042014-06-09 19:50:00 -07002547 long ident = Binder.clearCallingIdentity();
2548 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002549 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2550 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002551 } finally {
2552 Binder.restoreCallingIdentity(ident);
2553 }
2554 }
2555
2556 // IJobScheduler implementation
2557 @Override
2558 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2559 if (DEBUG) {
2560 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2561 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002562 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002563 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002564
2565 enforceValidJobRequest(uid, job);
2566 if (job.isPersisted()) {
2567 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2568 }
2569 if (work == null) {
2570 throw new NullPointerException("work is null");
2571 }
2572
Makoto Onuki959acb52018-01-26 14:10:03 -08002573 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002574
2575 long ident = Binder.clearCallingIdentity();
2576 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002577 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2578 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002579 } finally {
2580 Binder.restoreCallingIdentity(ident);
2581 }
2582 }
2583
2584 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002585 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002586 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002587 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002588 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002589 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002590 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002591 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002592
2593 if (packageName == null) {
2594 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002595 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002596
2597 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2598 android.Manifest.permission.UPDATE_DEVICE_STATS);
2599 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2600 throw new SecurityException("Caller uid " + callerUid
2601 + " not permitted to schedule jobs for other apps");
2602 }
2603
Makoto Onuki959acb52018-01-26 14:10:03 -08002604 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002605
Shreyas Basarge968ac752016-01-11 23:09:26 +00002606 long ident = Binder.clearCallingIdentity();
2607 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002608 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002609 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002610 } finally {
2611 Binder.restoreCallingIdentity(ident);
2612 }
2613 }
2614
2615 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002616 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2617 final int uid = Binder.getCallingUid();
2618
2619 long ident = Binder.clearCallingIdentity();
2620 try {
2621 return JobSchedulerService.this.getPendingJobs(uid);
2622 } finally {
2623 Binder.restoreCallingIdentity(ident);
2624 }
2625 }
2626
2627 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002628 public JobInfo getPendingJob(int jobId) throws RemoteException {
2629 final int uid = Binder.getCallingUid();
2630
2631 long ident = Binder.clearCallingIdentity();
2632 try {
2633 return JobSchedulerService.this.getPendingJob(uid, jobId);
2634 } finally {
2635 Binder.restoreCallingIdentity(ident);
2636 }
2637 }
2638
2639 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002640 public void cancelAll() throws RemoteException {
2641 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002642 long ident = Binder.clearCallingIdentity();
2643 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002644 JobSchedulerService.this.cancelJobsForUid(uid,
2645 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002646 } finally {
2647 Binder.restoreCallingIdentity(ident);
2648 }
2649 }
2650
2651 @Override
2652 public void cancel(int jobId) throws RemoteException {
2653 final int uid = Binder.getCallingUid();
2654
2655 long ident = Binder.clearCallingIdentity();
2656 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002657 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002658 } finally {
2659 Binder.restoreCallingIdentity(ident);
2660 }
2661 }
2662
2663 /**
2664 * "dumpsys" infrastructure
2665 */
2666 @Override
2667 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002668 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002669
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002670 int filterUid = -1;
2671 boolean proto = false;
2672 if (!ArrayUtils.isEmpty(args)) {
2673 int opti = 0;
2674 while (opti < args.length) {
2675 String arg = args[opti];
2676 if ("-h".equals(arg)) {
2677 dumpHelp(pw);
2678 return;
2679 } else if ("-a".equals(arg)) {
2680 // Ignore, we always dump all.
2681 } else if ("--proto".equals(arg)) {
2682 proto = true;
2683 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2684 pw.println("Unknown option: " + arg);
2685 return;
2686 } else {
2687 break;
2688 }
2689 opti++;
2690 }
2691 if (opti < args.length) {
2692 String pkg = args[opti];
2693 try {
2694 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2695 PackageManager.MATCH_ANY_USER);
2696 } catch (NameNotFoundException ignored) {
2697 pw.println("Invalid package: " + pkg);
2698 return;
2699 }
2700 }
2701 }
2702
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002703 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07002704 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002705 if (proto) {
2706 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2707 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002708 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
2709 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002710 }
Christopher Tate7060b042014-06-09 19:50:00 -07002711 } finally {
2712 Binder.restoreCallingIdentity(identityToken);
2713 }
2714 }
Christopher Tate5d346052016-03-08 12:56:08 -08002715
2716 @Override
2717 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002718 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002719 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002720 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002721 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002722 };
2723
Christopher Tate5d346052016-03-08 12:56:08 -08002724 // Shell command infrastructure: run the given job immediately
2725 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2726 if (DEBUG) {
2727 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2728 + " " + jobId + " f=" + force);
2729 }
2730
2731 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002732 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2733 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002734 if (uid < 0) {
2735 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2736 }
2737
2738 synchronized (mLock) {
2739 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2740 if (js == null) {
2741 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2742 }
2743
2744 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2745 if (!js.isConstraintsSatisfied()) {
2746 js.overrideState = 0;
2747 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2748 }
2749
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002750 queueReadyJobsForExecutionLocked();
2751 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002752 }
2753 } catch (RemoteException e) {
2754 // can't happen
2755 }
2756 return 0;
2757 }
2758
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002759 // Shell command infrastructure: immediately timeout currently executing jobs
2760 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2761 boolean hasJobId, int jobId) {
2762 if (DEBUG) {
2763 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2764 }
2765
2766 synchronized (mLock) {
2767 boolean foundSome = false;
2768 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002769 final JobServiceContext jc = mActiveServices.get(i);
2770 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002771 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002772 foundSome = true;
2773 pw.print("Timing out: ");
2774 js.printUniqueId(pw);
2775 pw.print(" ");
2776 pw.println(js.getServiceComponent().flattenToShortString());
2777 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002778 }
2779 if (!foundSome) {
2780 pw.println("No matching executing jobs found.");
2781 }
2782 }
2783 return 0;
2784 }
2785
Christopher Tate8c67d122017-09-29 16:54:26 -07002786 // Shell command infrastructure: cancel a scheduled job
2787 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2788 boolean hasJobId, int jobId) {
2789 if (DEBUG) {
2790 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2791 }
2792
2793 int pkgUid = -1;
2794 try {
2795 IPackageManager pm = AppGlobals.getPackageManager();
2796 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2797 } catch (RemoteException e) { /* can't happen */ }
2798
2799 if (pkgUid < 0) {
2800 pw.println("Package " + pkgName + " not found.");
2801 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2802 }
2803
2804 if (!hasJobId) {
2805 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2806 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2807 pw.println("No matching jobs found.");
2808 }
2809 } else {
2810 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002811 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002812 pw.println("No matching job found.");
2813 }
2814 }
2815
2816 return 0;
2817 }
2818
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002819 void setMonitorBattery(boolean enabled) {
2820 synchronized (mLock) {
2821 if (mBatteryController != null) {
2822 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2823 }
2824 }
2825 }
2826
2827 int getBatterySeq() {
2828 synchronized (mLock) {
2829 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2830 }
2831 }
2832
2833 boolean getBatteryCharging() {
2834 synchronized (mLock) {
2835 return mBatteryController != null
2836 ? mBatteryController.getTracker().isOnStablePower() : false;
2837 }
2838 }
2839
2840 boolean getBatteryNotLow() {
2841 synchronized (mLock) {
2842 return mBatteryController != null
2843 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2844 }
2845 }
2846
Dianne Hackborn532ea262017-03-17 17:50:55 -07002847 int getStorageSeq() {
2848 synchronized (mLock) {
2849 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2850 }
2851 }
2852
2853 boolean getStorageNotLow() {
2854 synchronized (mLock) {
2855 return mStorageController != null
2856 ? mStorageController.getTracker().isStorageNotLow() : false;
2857 }
2858 }
2859
Christopher Tatea732f012017-10-26 17:26:53 -07002860 long getCurrentHeartbeat() {
2861 synchronized (mLock) {
2862 return mHeartbeat;
2863 }
2864 }
2865
Christopher Tated1aebb32018-01-31 13:24:14 -08002866 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07002867 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2868 try {
2869 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2870 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2871 if (uid < 0) {
2872 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2873 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2874 }
2875
2876 synchronized (mLock) {
2877 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2878 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2879 if (js == null) {
2880 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2881 pw.print("/jid"); pw.print(jobId); pw.println(")");
2882 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2883 }
2884
2885 boolean printed = false;
2886 if (mPendingJobs.contains(js)) {
2887 pw.print("pending");
2888 printed = true;
2889 }
2890 if (isCurrentlyActiveLocked(js)) {
2891 if (printed) {
2892 pw.print(" ");
2893 }
2894 printed = true;
2895 pw.println("active");
2896 }
2897 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2898 if (printed) {
2899 pw.print(" ");
2900 }
2901 printed = true;
2902 pw.println("user-stopped");
2903 }
2904 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2905 if (printed) {
2906 pw.print(" ");
2907 }
2908 printed = true;
2909 pw.println("backing-up");
2910 }
2911 boolean componentPresent = false;
2912 try {
2913 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2914 js.getServiceComponent(),
2915 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2916 js.getUserId()) != null);
2917 } catch (RemoteException e) {
2918 }
2919 if (!componentPresent) {
2920 if (printed) {
2921 pw.print(" ");
2922 }
2923 printed = true;
2924 pw.println("no-component");
2925 }
2926 if (js.isReady()) {
2927 if (printed) {
2928 pw.print(" ");
2929 }
2930 printed = true;
2931 pw.println("ready");
2932 }
2933 if (!printed) {
2934 pw.print("waiting");
2935 }
2936 pw.println();
2937 }
2938 } catch (RemoteException e) {
2939 // can't happen
2940 }
2941 return 0;
2942 }
2943
Christopher Tated1aebb32018-01-31 13:24:14 -08002944 // Shell command infrastructure
2945 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
2946 if (numBeats < 1) {
2947 pw.println(getCurrentHeartbeat());
2948 return 0;
2949 }
2950
2951 pw.print("Advancing standby heartbeat by ");
2952 pw.println(numBeats);
2953 synchronized (mLock) {
2954 advanceHeartbeatLocked(numBeats);
2955 }
2956 return 0;
2957 }
2958
Shreyas Basarge5db09082016-01-07 13:38:29 +00002959 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2960 StringBuilder s = new StringBuilder(initial + ": ");
2961 for (int i=0; i<map.length; i++) {
2962 s.append("(")
2963 .append(map[i] == null? -1: map[i].getJobId())
2964 .append(map[i] == null? -1: map[i].getUid())
2965 .append(")" );
2966 }
2967 return s.toString();
2968 }
2969
2970 private String printPendingQueue() {
2971 StringBuilder s = new StringBuilder("Pending queue: ");
2972 Iterator<JobStatus> it = mPendingJobs.iterator();
2973 while (it.hasNext()) {
2974 JobStatus js = it.next();
2975 s.append("(")
2976 .append(js.getJob().getId())
2977 .append(", ")
2978 .append(js.getUid())
2979 .append(") ");
2980 }
2981 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07002982 }
Christopher Tate7060b042014-06-09 19:50:00 -07002983
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002984 static void dumpHelp(PrintWriter pw) {
2985 pw.println("Job Scheduler (jobscheduler) dump options:");
2986 pw.println(" [-h] [package] ...");
2987 pw.println(" -h: print this help");
2988 pw.println(" [package] is an optional package name to limit the output to.");
2989 }
2990
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002991 /** Sort jobs by caller UID, then by Job ID. */
2992 private static void sortJobs(List<JobStatus> jobs) {
2993 Collections.sort(jobs, new Comparator<JobStatus>() {
2994 @Override
2995 public int compare(JobStatus o1, JobStatus o2) {
2996 int uid1 = o1.getUid();
2997 int uid2 = o2.getUid();
2998 int id1 = o1.getJobId();
2999 int id2 = o2.getJobId();
3000 if (uid1 != uid2) {
3001 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003002 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003003 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003004 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003005 });
3006 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003007
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003008 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003009 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003010 final long nowElapsed = sElapsedRealtimeClock.millis();
3011 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003012 final Predicate<JobStatus> predicate = (js) -> {
3013 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3014 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3015 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003016 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003017 mConstants.dump(pw);
3018 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003019
3020 pw.println(" Heartbeat:");
3021 pw.print(" Current: "); pw.println(mHeartbeat);
3022 pw.println(" Next");
3023 pw.print(" ACTIVE: "); pw.println(mNextBucketHeartbeat[0]);
3024 pw.print(" WORKING: "); pw.println(mNextBucketHeartbeat[1]);
3025 pw.print(" FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3026 pw.print(" RARE: "); pw.println(mNextBucketHeartbeat[3]);
3027 pw.print(" Last heartbeat: ");
3028 TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3029 pw.println();
3030 pw.print(" Next heartbeat: ");
3031 TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3032 nowElapsed, pw);
3033 pw.println();
3034 pw.println();
3035
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003036 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003037 pw.print("Registered ");
3038 pw.print(mJobs.size());
3039 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003040 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003041 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003042 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003043 for (JobStatus job : jobs) {
3044 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3045 pw.println(job.toShortStringExceptUniqueId());
3046
3047 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003048 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003049 continue;
3050 }
3051
Dianne Hackborn6d068262017-05-16 13:14:37 -07003052 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003053 pw.print(" Last run heartbeat: ");
3054 pw.print(heartbeatWhenJobsLastRun(job));
3055 pw.println();
3056
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003057 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003058 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003059 pw.print(" (job=");
3060 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003061 pw.print(" user=");
3062 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003063 pw.print(" !pending=");
3064 pw.print(!mPendingJobs.contains(job));
3065 pw.print(" !active=");
3066 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003067 pw.print(" !backingup=");
3068 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003069 pw.print(" comp=");
3070 boolean componentPresent = false;
3071 try {
3072 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3073 job.getServiceComponent(),
3074 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3075 job.getUserId()) != null);
3076 } catch (RemoteException e) {
3077 }
3078 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003079 pw.println(")");
3080 }
Christopher Tate7060b042014-06-09 19:50:00 -07003081 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003082 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003083 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003084 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003085 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003086 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3087 pw.increaseIndent();
3088 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3089 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003090 }
3091 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003092 pw.println("Uid priority overrides:");
3093 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003094 int uid = mUidPriorityOverride.keyAt(i);
3095 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3096 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3097 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3098 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003099 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003100 if (mBackingUpUids.size() > 0) {
3101 pw.println();
3102 pw.println("Backing up uids:");
3103 boolean first = true;
3104 for (int i = 0; i < mBackingUpUids.size(); i++) {
3105 int uid = mBackingUpUids.keyAt(i);
3106 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3107 if (first) {
3108 pw.print(" ");
3109 first = false;
3110 } else {
3111 pw.print(", ");
3112 }
3113 pw.print(UserHandle.formatUid(uid));
3114 }
3115 }
3116 pw.println();
3117 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003118 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003119 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003120 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003121 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3122 pw.println();
3123 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003124 pw.println("Pending queue:");
3125 for (int i=0; i<mPendingJobs.size(); i++) {
3126 JobStatus job = mPendingJobs.get(i);
3127 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3128 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003129 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003130 int priority = evaluateJobPriorityLocked(job);
3131 if (priority != JobInfo.PRIORITY_DEFAULT) {
3132 pw.print(" Evaluated priority: "); pw.println(priority);
3133 }
3134 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003135 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003136 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003137 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003138 }
Christopher Tate7060b042014-06-09 19:50:00 -07003139 pw.println();
3140 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003141 for (int i=0; i<mActiveServices.size(); i++) {
3142 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003143 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003144 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003145 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003146 if (jsc.mStoppedReason != null) {
3147 pw.print("inactive since ");
3148 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3149 pw.print(", stopped because: ");
3150 pw.println(jsc.mStoppedReason);
3151 } else {
3152 pw.println("inactive");
3153 }
Christopher Tate7060b042014-06-09 19:50:00 -07003154 continue;
3155 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003156 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003157 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003158 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003159 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003160 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003161 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003162 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003163 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003164 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08003165 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003166 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003167 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003168 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003169 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003170 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3171 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003172 }
3173 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003174 if (filterUid == -1) {
3175 pw.println();
3176 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3177 pw.print("mReportedActive="); pw.println(mReportedActive);
3178 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3179 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003180 pw.println();
3181 pw.print("PersistStats: ");
3182 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003183 }
3184 pw.println();
3185 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003186
3187 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3188 ProtoOutputStream proto = new ProtoOutputStream(fd);
3189 final int filterUidFinal = UserHandle.getAppId(filterUid);
3190 final long nowElapsed = sElapsedRealtimeClock.millis();
3191 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003192 final Predicate<JobStatus> predicate = (js) -> {
3193 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3194 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3195 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003196
3197 synchronized (mLock) {
3198 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003199 proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3200 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3201 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3202 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3203 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3204 proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3205 mLastHeartbeatTime - nowUptime);
3206 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3207 mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
3208
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003209 for (int u : mStartedUsers) {
3210 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3211 }
3212 if (mJobs.size() > 0) {
3213 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3214 sortJobs(jobs);
3215 for (JobStatus job : jobs) {
3216 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3217 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3218
3219 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003220 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003221 continue;
3222 }
3223
3224 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3225
3226 // isReadyToBeExecuted
3227 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3228 job.isReady());
3229 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3230 ArrayUtils.contains(mStartedUsers, job.getUserId()));
3231 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3232 mPendingJobs.contains(job));
3233 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3234 isCurrentlyActiveLocked(job));
3235 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3236 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3237 boolean componentPresent = false;
3238 try {
3239 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3240 job.getServiceComponent(),
3241 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3242 job.getUserId()) != null);
3243 } catch (RemoteException e) {
3244 }
3245 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3246 componentPresent);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003247 proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003248
3249 proto.end(rjToken);
3250 }
3251 }
3252 for (StateController controller : mControllers) {
3253 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003254 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003255 }
3256 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3257 int uid = mUidPriorityOverride.keyAt(i);
3258 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3259 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3260 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3261 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3262 mUidPriorityOverride.valueAt(i));
3263 proto.end(pToken);
3264 }
3265 }
3266 for (int i = 0; i < mBackingUpUids.size(); i++) {
3267 int uid = mBackingUpUids.keyAt(i);
3268 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3269 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3270 }
3271 }
3272
3273 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3274 filterUidFinal);
3275 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3276 filterUidFinal);
3277
3278 for (JobStatus job : mPendingJobs) {
3279 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3280
3281 job.writeToShortProto(proto, PendingJob.INFO);
3282 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3283 int priority = evaluateJobPriorityLocked(job);
3284 if (priority != JobInfo.PRIORITY_DEFAULT) {
3285 proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3286 }
3287 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3288
3289 proto.end(pjToken);
3290 }
3291 for (JobServiceContext jsc : mActiveServices) {
3292 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3293 final JobStatus job = jsc.getRunningJobLocked();
3294
3295 if (job == null) {
3296 final long ijToken = proto.start(ActiveJob.INACTIVE);
3297
3298 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3299 nowElapsed - jsc.mStoppedTime);
3300 if (jsc.mStoppedReason != null) {
3301 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3302 jsc.mStoppedReason);
3303 }
3304
3305 proto.end(ijToken);
3306 } else {
3307 final long rjToken = proto.start(ActiveJob.RUNNING);
3308
3309 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3310
3311 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3312 nowElapsed - jsc.getExecutionStartTimeElapsed());
3313 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3314 jsc.getTimeoutElapsed() - nowElapsed);
3315
3316 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3317
3318 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3319 if (priority != JobInfo.PRIORITY_DEFAULT) {
3320 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3321 }
3322
3323 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3324 nowUptime - job.madeActive);
3325 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3326 job.madeActive - job.madePending);
3327
3328 proto.end(rjToken);
3329 }
3330 proto.end(ajToken);
3331 }
3332 if (filterUid == -1) {
3333 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3334 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3335 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3336 }
3337 }
3338
3339 proto.flush();
3340 }
Christopher Tate7060b042014-06-09 19:50:00 -07003341}