blob: 260633ac4febef3b0e31aa936b92599f9ad2bd16 [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;
Serik Beketayev75915d12018-08-01 16:56:59 -070035import android.app.job.JobSnapshot;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070036import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080037import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070038import android.app.usage.UsageStatsManagerInternal;
39import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070042import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070043import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070046import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070047import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060048import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070049import android.content.pm.PackageManagerInternal;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070050import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070051import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070052import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070053import android.os.BatteryStats;
Amith Yamasani977e11f2018-02-16 11:29:54 -080054import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070055import android.os.Binder;
56import android.os.Handler;
57import android.os.Looper;
58import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070059import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070060import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080061import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070062import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070063import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070064import android.os.SystemClock;
65import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070066import android.os.UserManagerInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070067import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080068import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070069import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070070import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070071import android.util.Slog;
72import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080073import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080074import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080075import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080076import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080077
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070078import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070079import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070080import com.android.internal.app.procstats.ProcessStats;
Christopher Tatea732f012017-10-26 17:26:53 -070081import com.android.internal.os.BackgroundThread;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070082import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060083import com.android.internal.util.DumpUtils;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070084import com.android.internal.util.IndentingPrintWriter;
Makoto Onuki15407842018-01-19 14:23:11 -080085import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070086import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080087import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070088import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080089import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080090import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
91import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Makoto Onukib5d5e972018-02-20 14:44:20 -080092import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070093import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070094import com.android.server.job.controllers.BatteryController;
95import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080096import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070097import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070098import com.android.server.job.controllers.IdleController;
99import com.android.server.job.controllers.JobStatus;
100import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700101import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700102import com.android.server.job.controllers.TimeController;
103
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700104import libcore.util.EmptyArray;
105
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700106import java.io.FileDescriptor;
107import java.io.PrintWriter;
108import java.time.Clock;
109import java.util.ArrayList;
110import java.util.Arrays;
111import java.util.Collections;
112import java.util.Comparator;
Christopher Tate325768c2018-03-07 16:07:56 -0800113import java.util.HashMap;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700114import java.util.Iterator;
115import java.util.List;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700116import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800117import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700118
Christopher Tate7060b042014-06-09 19:50:00 -0700119/**
120 * Responsible for taking jobs representing work to be performed by a client app, and determining
121 * based on the criteria specified when that job should be run against the client application's
122 * endpoint.
123 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
124 * about constraints, or the state of active jobs. It receives callbacks from the various
125 * controllers and completed jobs and operates accordingly.
126 *
127 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
128 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
129 * @hide
130 */
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600131public class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700132 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700133 public static final String TAG = "JobScheduler";
134 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700135 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800136
Dianne Hackborn970510b2016-02-24 16:56:42 -0800137 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700138 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800139 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800140 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800141 /** The maximum number of jobs that we allow an unprivileged app to schedule */
142 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700143
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700144 @VisibleForTesting
145 public static Clock sSystemClock = Clock.systemUTC();
146 @VisibleForTesting
147 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
148 @VisibleForTesting
149 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800150
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800151 /** Global local for all job scheduler state. */
152 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700153 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700154 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700155 /** Tracking the standby bucket state of each app */
156 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700157 /** Tracking amount of time each package runs for. */
158 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700159
160 static final int MSG_JOB_EXPIRED = 0;
161 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700162 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000163 static final int MSG_CHECK_JOB_GREEDY = 3;
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800164 static final int MSG_UID_STATE_CHANGED = 4;
165 static final int MSG_UID_GONE = 5;
166 static final int MSG_UID_ACTIVE = 6;
167 static final int MSG_UID_IDLE = 7;
Christopher Tate7060b042014-06-09 19:50:00 -0700168
Christopher Tate7060b042014-06-09 19:50:00 -0700169 /**
170 * Track Services that have currently active or pending jobs. The index is provided by
171 * {@link JobStatus#getServiceToken()}
172 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700173 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700174
Christopher Tate7060b042014-06-09 19:50:00 -0700175 /** List of controllers that will notify this service of updates to jobs. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700176 private final List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800177 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700178 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700179 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700180 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700181 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700182 private final DeviceIdleJobsController mDeviceIdleJobsController;
183
Christopher Tate7060b042014-06-09 19:50:00 -0700184 /**
185 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
186 * when ready to execute them.
187 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700188 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700189
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700190 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700191
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700192 final JobHandler mHandler;
193 final JobSchedulerStub mJobSchedulerStub;
194
Christopher Tatea732f012017-10-26 17:26:53 -0700195 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800196 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700197 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800198 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800199 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800200 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700201
202 /**
203 * Set to true once we are allowed to run third party apps.
204 */
205 boolean mReadyToRock;
206
Christopher Tate7060b042014-06-09 19:50:00 -0700207 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800208 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800209 */
210 boolean mReportedActive;
211
212 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800213 * Are we currently in device-wide standby parole?
214 */
215 volatile boolean mInParole;
216
217 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800218 * Current limit on the number of concurrent JobServiceContext entries we want to
219 * keep actively running a job.
220 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700221 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800222
223 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800224 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800225 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800226 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
227
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700228 /**
229 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
230 */
231 final SparseIntArray mBackingUpUids = new SparseIntArray();
232
Christopher Tatea732f012017-10-26 17:26:53 -0700233 /**
234 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
235 * next become runnable. Index into this array is by normalized bucket:
236 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
237 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
238 * and NEVER apps don't get them at all.
239 */
240 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
241 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800242 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
243
Christopher Tate325768c2018-03-07 16:07:56 -0800244 /**
245 * Named indices into the STANDBY_BEATS array, for clarity in referring to
246 * specific buckets' bookkeeping.
247 */
248 static final int ACTIVE_INDEX = 0;
249 static final int WORKING_INDEX = 1;
250 static final int FREQUENT_INDEX = 2;
251 static final int RARE_INDEX = 3;
Christopher Tatef2159712018-03-27 16:04:14 -0700252 static final int NEVER_INDEX = 4;
Christopher Tate325768c2018-03-07 16:07:56 -0800253
254 /**
255 * Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
256 * rather than rely on Usage Stats' timestamps, because heartbeat time can be
257 * manipulated for testing purposes and we need job runnability to track that rather
258 * than real time.
259 *
260 * Outer SparseArray slices by user handle; inner map of package name to heartbeat
261 * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
262 * and it will be accessed in a known-hot code path.
263 */
264 final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
265
Christopher Tated1aebb32018-01-31 13:24:14 -0800266 static final String HEARTBEAT_TAG = "*job.heartbeat*";
267 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700268
Dianne Hackborn970510b2016-02-24 16:56:42 -0800269 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
270
271 /**
272 * This array essentially stores the state of mActiveServices array.
273 * The ith index stores the job present on the ith JobServiceContext.
274 * We manipulate this array until we arrive at what jobs should be running on
275 * what JobServiceContext.
276 */
277 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
278 /**
279 * Indicates whether we need to act on this jobContext id
280 */
281 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
282 /**
283 * The uid whose jobs we would like to assign to a context.
284 */
285 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800286
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700287 private class ConstantsObserver extends ContentObserver {
288 private ContentResolver mResolver;
289
290 public ConstantsObserver(Handler handler) {
291 super(handler);
292 }
293
294 public void start(ContentResolver resolver) {
295 mResolver = resolver;
296 mResolver.registerContentObserver(Settings.Global.getUriFor(
297 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
298 updateConstants();
299 }
300
301 @Override
302 public void onChange(boolean selfChange, Uri uri) {
303 updateConstants();
304 }
305
306 private void updateConstants() {
307 synchronized (mLock) {
308 try {
309 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
310 Settings.Global.JOB_SCHEDULER_CONSTANTS));
311 } catch (IllegalArgumentException e) {
312 // Failed to parse the settings string, log this and move on
313 // with defaults.
314 Slog.e(TAG, "Bad jobscheduler settings", e);
315 }
316 }
317
318 // Reset the heartbeat alarm based on the new heartbeat duration
319 setNextHeartbeatAlarm();
320 }
321 }
322
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800323 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700324 * All times are in milliseconds. These constants are kept synchronized with the system
325 * global Settings. Any access to this class or its fields should be done while
326 * holding the JobSchedulerService.mLock lock.
327 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700328 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700329 // Key names stored in the settings value.
330 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
331 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800332 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700333 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700334 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
335 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
336 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
337 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
338 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
339 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
340 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
341 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
342 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
343 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700344 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
345 = "max_standard_reschedule_count";
346 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
347 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
348 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700349 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
350 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
351 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
352 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700353 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
354 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700355
356 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
357 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800358 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700359 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700360 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
361 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
362 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
363 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
364 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
365 private static final int DEFAULT_FG_JOB_COUNT = 4;
366 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
367 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700368 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700369 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700370 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
371 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
372 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
373 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700374 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000375 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
376 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700377 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700378 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
379 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700380
381 /**
382 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
383 * early.
384 */
385 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
386 /**
387 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
388 * things early.
389 */
390 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
391 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800392 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
393 * schedule things early.
394 */
395 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
396 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700397 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
398 * schedule things early.
399 */
400 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
401 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700402 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
403 * things early. 1 == Run connectivity jobs as soon as ready.
404 */
405 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
406 /**
407 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
408 * schedule things early.
409 */
410 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
411 /**
412 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
413 * running some work early. This (and thus the other min counts) is now set to 1, to
414 * prevent any batching at this level. Since we now do batching through doze, that is
415 * a much better mechanism.
416 */
417 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
418 /**
419 * This is the job execution factor that is considered to be heavy use of the system.
420 */
421 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
422 /**
423 * This is the job execution factor that is considered to be moderate use of the system.
424 */
425 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
426 /**
427 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
428 */
429 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
430 /**
431 * The maximum number of background jobs we allow when the system is in a normal
432 * memory state.
433 */
434 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
435 /**
436 * The maximum number of background jobs we allow when the system is in a moderate
437 * memory state.
438 */
439 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
440 /**
441 * The maximum number of background jobs we allow when the system is in a low
442 * memory state.
443 */
444 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
445 /**
446 * The maximum number of background jobs we allow when the system is in a critical
447 * memory state.
448 */
449 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700450 /**
451 * The maximum number of times we allow a job to have itself rescheduled before
452 * giving up on it, for standard jobs.
453 */
454 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
455 /**
456 * The maximum number of times we allow a job to have itself rescheduled before
457 * giving up on it, for jobs that are executing work.
458 */
459 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
460 /**
461 * The minimum backoff time to allow for linear backoff.
462 */
463 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
464 /**
465 * The minimum backoff time to allow for exponential backoff.
466 */
467 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700468 /**
469 * How often we recalculate runnability based on apps' standby bucket assignment.
470 * This should be prime relative to common time interval lengths such as a quarter-
471 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
472 */
473 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700474 /**
475 * Mapping: standby bucket -> number of heartbeats between each sweep of that
476 * bucket's jobs.
477 *
478 * Bucket assignments as recorded in the JobStatus objects are normalized to be
479 * indices into this array, rather than the raw constants used
480 * by AppIdleHistory.
481 */
482 final int[] STANDBY_BEATS = {
483 0,
484 DEFAULT_STANDBY_WORKING_BEATS,
485 DEFAULT_STANDBY_FREQUENT_BEATS,
486 DEFAULT_STANDBY_RARE_BEATS
487 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700488 /**
489 * The fraction of a job's running window that must pass before we
490 * consider running it when the network is congested.
491 */
492 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
493 /**
494 * The fraction of a prefetch job's running window that must pass before
495 * we consider matching it against a metered network.
496 */
497 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700498
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700499 private final KeyValueListParser mParser = new KeyValueListParser(',');
500
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700501 void updateConstantsLocked(String value) {
502 try {
503 mParser.setString(value);
504 } catch (Exception e) {
505 // Failed to parse the settings string, log this and move on
506 // with defaults.
507 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700508 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800509
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700510 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
511 DEFAULT_MIN_IDLE_COUNT);
512 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
513 DEFAULT_MIN_CHARGING_COUNT);
514 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
515 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
516 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
517 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
518 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
519 DEFAULT_MIN_CONNECTIVITY_COUNT);
520 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
521 DEFAULT_MIN_CONTENT_COUNT);
522 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
523 DEFAULT_MIN_READY_JOBS_COUNT);
524 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
525 DEFAULT_HEAVY_USE_FACTOR);
526 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
527 DEFAULT_MODERATE_USE_FACTOR);
528 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
529 DEFAULT_FG_JOB_COUNT);
530 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
531 DEFAULT_BG_NORMAL_JOB_COUNT);
532 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
533 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
534 }
535 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
536 DEFAULT_BG_MODERATE_JOB_COUNT);
537 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
538 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
539 }
540 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
541 DEFAULT_BG_LOW_JOB_COUNT);
542 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
543 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
544 }
545 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
546 DEFAULT_BG_CRITICAL_JOB_COUNT);
547 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
548 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
549 }
550 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
551 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
552 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
553 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
554 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
555 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
556 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
557 DEFAULT_MIN_EXP_BACKOFF_TIME);
558 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
559 DEFAULT_STANDBY_HEARTBEAT_TIME);
Christopher Tate325768c2018-03-07 16:07:56 -0800560 STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700561 DEFAULT_STANDBY_WORKING_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800562 STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700563 DEFAULT_STANDBY_FREQUENT_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800564 STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700565 DEFAULT_STANDBY_RARE_BEATS);
566 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
567 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
568 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
569 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700570 }
571
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700572 void dump(IndentingPrintWriter pw) {
573 pw.println("Settings:");
574 pw.increaseIndent();
575 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
576 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
577 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
578 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
579 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
580 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
581 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
582 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
583 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
584 pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
585 pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
586 pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
587 pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
588 pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
589 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
590 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
591 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
592 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
593 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
594 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700595 pw.print(STANDBY_BEATS[0]);
596 for (int i = 1; i < STANDBY_BEATS.length; i++) {
597 pw.print(", ");
598 pw.print(STANDBY_BEATS[i]);
599 }
600 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700601 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
602 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
603 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700604 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800605
606 void dump(ProtoOutputStream proto, long fieldId) {
607 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800608 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
609 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
610 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
611 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
612 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
613 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
614 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
615 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
616 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
617 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
618 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
619 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
620 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
621 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
622 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
623 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
624 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
625 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
626 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800627 for (int period : STANDBY_BEATS) {
628 proto.write(ConstantsProto.STANDBY_BEATS, period);
629 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700630 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
631 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800632 proto.end(token);
633 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700634 }
635
636 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700637 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700638
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700639 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
640 if (o1.enqueueTime < o2.enqueueTime) {
641 return -1;
642 }
643 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
644 };
645
646 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
647 int where = Collections.binarySearch(array, newItem, comparator);
648 if (where < 0) {
649 where = ~where;
650 }
651 array.add(where, newItem);
652 }
653
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700654 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700655 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
656 * still clean up. On reinstall the package will have a new uid.
657 */
658 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
659 @Override
660 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700661 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700662 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700663 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700664 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700665 final String pkgName = getPackageName(intent);
666 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
667
Christopher Tateee7805b2016-07-15 16:56:56 -0700668 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700669 // Purge the app's jobs if the whole package was just disabled. When this is
670 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700671 if (pkgName != null && pkgUid != -1) {
672 final String[] changedComponents = intent.getStringArrayExtra(
673 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
674 if (changedComponents != null) {
675 for (String component : changedComponents) {
676 if (component.equals(pkgName)) {
677 if (DEBUG) {
678 Slog.d(TAG, "Package state change: " + pkgName);
679 }
680 try {
681 final int userId = UserHandle.getUserId(pkgUid);
682 IPackageManager pm = AppGlobals.getPackageManager();
683 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
684 if (state == COMPONENT_ENABLED_STATE_DISABLED
685 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
686 if (DEBUG) {
687 Slog.d(TAG, "Removing jobs for package " + pkgName
688 + " in user " + userId);
689 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700690 cancelJobsForPackageAndUid(pkgName, pkgUid,
691 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700692 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700693 } catch (RemoteException|IllegalArgumentException e) {
694 /*
695 * IllegalArgumentException means that the package doesn't exist.
696 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
697 * behind outright uninstall, so by the time we try to act it's gone.
698 * We don't need to act on this PACKAGE_CHANGED when this happens;
699 * we'll get a PACKAGE_REMOVED later and clean up then.
700 *
701 * RemoteException can't actually happen; the package manager is
702 * running in this same process.
703 */
704 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700705 break;
706 }
707 }
708 }
709 } else {
710 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
711 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700712 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700713 // If this is an outright uninstall rather than the first half of an
714 // app update sequence, cancel the jobs associated with the app.
715 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
716 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
717 if (DEBUG) {
718 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
719 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700720 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Christopher Tate7060b042014-06-09 19:50:00 -0700721 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700722 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700723 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
724 if (DEBUG) {
725 Slog.d(TAG, "Removing jobs for user: " + userId);
726 }
727 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700728 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
729 // Has this package scheduled any jobs, such that we will take action
730 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700731 if (pkgUid != -1) {
732 List<JobStatus> jobsForUid;
733 synchronized (mLock) {
734 jobsForUid = mJobs.getJobsByUid(pkgUid);
735 }
736 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
737 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
738 if (DEBUG) {
739 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
740 + pkgUid + " has jobs");
741 }
742 setResultCode(Activity.RESULT_OK);
743 break;
744 }
745 }
746 }
747 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
748 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700749 if (pkgUid != -1) {
750 if (DEBUG) {
751 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
752 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700753 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700754 }
Christopher Tate7060b042014-06-09 19:50:00 -0700755 }
756 }
757 };
758
Christopher Tateb5c07882016-05-26 17:11:09 -0700759 private String getPackageName(Intent intent) {
760 Uri uri = intent.getData();
761 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
762 return pkg;
763 }
764
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700765 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700766 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800767 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700768 }
769
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700770 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800771 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700772 }
773
774 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800775 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700776 }
777
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700778 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800779 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700780 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700781
782 @Override public void onUidCachedChanged(int uid, boolean cached) {
783 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700784 };
785
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600786 public Context getTestableContext() {
787 return getContext();
788 }
789
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800790 public Object getLock() {
791 return mLock;
792 }
793
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700794 public JobStore getJobStore() {
795 return mJobs;
796 }
797
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700798 public Constants getConstants() {
799 return mConstants;
800 }
801
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700802 @Override
803 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700804 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
805 // Let's kick any outstanding jobs for this user.
806 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
807 }
808
809 @Override
810 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700811 // Let's kick any outstanding jobs for this user.
812 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
813 }
814
815 @Override
816 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700817 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700818 }
819
Makoto Onuki15407842018-01-19 14:23:11 -0800820 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800821 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800822 */
Makoto Onukie4918212018-02-06 11:30:15 -0800823 private boolean isUidActive(int uid) {
824 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800825 }
826
Makoto Onukie4918212018-02-06 11:30:15 -0800827 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800828
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700829 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
830 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700831 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800832 if (ActivityManager.getService().isAppStartModeDisabled(uId,
833 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700834 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
835 + " -- package not allowed to start");
836 return JobScheduler.RESULT_FAILURE;
837 }
838 } catch (RemoteException e) {
839 }
Christopher Tatea732f012017-10-26 17:26:53 -0700840
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800841 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700842 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
843
844 if (work != null && toCancel != null) {
845 // Fast path: we are adding work to an existing job, and the JobInfo is not
846 // changing. We can just directly enqueue this work in to the job.
847 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -0800848
Dianne Hackborn342e6032017-04-13 18:04:31 -0700849 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -0800850
851 // If any of work item is enqueued when the source is in the foreground,
852 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -0800853 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800854
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700855 return JobScheduler.RESULT_SUCCESS;
856 }
857 }
858
859 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -0800860
861 // Give exemption if the source is in the foreground just now.
862 // Note if it's a sync job, this method is called on the handler so it's not exactly
863 // the state when requestSync() was called, but that should be fine because of the
864 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -0800865 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800866
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700867 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800868 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800869 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800870 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
871 Slog.w(TAG, "Too many jobs for uid " + uId);
872 throw new IllegalStateException("Apps may not schedule more than "
873 + MAX_JOBS_PER_APP + " distinct jobs");
874 }
875 }
876
Dianne Hackborna47223f2017-03-30 13:49:13 -0700877 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700878 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700879
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700880 if (toCancel != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700881 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700882 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700883 if (work != null) {
884 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700885 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700886 }
887 startTrackingJobLocked(jobStatus, toCancel);
Tej Singhd5747a62018-01-08 20:57:35 -0800888 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -0800889 uId, null, jobStatus.getBatteryName(),
Tej Singh33a412b2018-03-16 18:43:59 -0700890 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
891 JobProtoEnums.STOP_REASON_CANCELLED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700892
893 // If the job is immediately ready to run, then we can just immediately
894 // put it in the pending list and try to schedule it. This is especially
895 // important for jobs with a 0 deadline constraint, since they will happen a fair
896 // amount, we want to handle them as quickly as possible, and semantically we want to
897 // make sure we have started holding the wake lock for the job before returning to
898 // the caller.
899 // If the job is not yet ready to run, there is nothing more to do -- we are
900 // now just waiting for one of its controllers to change state and schedule
901 // the job appropriately.
902 if (isReadyToBeExecutedLocked(jobStatus)) {
903 // This is a new job, we can just immediately put it on the pending
904 // list and try to run it.
905 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700906 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700907 maybeRunPendingJobsLocked();
908 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800909 }
Christopher Tate7060b042014-06-09 19:50:00 -0700910 return JobScheduler.RESULT_SUCCESS;
911 }
912
913 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800914 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800915 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
916 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
917 for (int i = jobs.size() - 1; i >= 0; i--) {
918 JobStatus job = jobs.get(i);
919 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700920 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800921 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700922 }
Christopher Tate7060b042014-06-09 19:50:00 -0700923 }
924
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600925 public JobInfo getPendingJob(int uid, int jobId) {
926 synchronized (mLock) {
927 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
928 for (int i = jobs.size() - 1; i >= 0; i--) {
929 JobStatus job = jobs.get(i);
930 if (job.getJobId() == jobId) {
931 return job.getJob();
932 }
933 }
934 return null;
935 }
936 }
937
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700938 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800939 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700940 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
941 for (int i=0; i<jobsForUser.size(); i++) {
942 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700943 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700944 }
Christopher Tate7060b042014-06-09 19:50:00 -0700945 }
946 }
947
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -0700948 private void cancelJobsForNonExistentUsers() {
949 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
950 synchronized (mLock) {
951 mJobs.removeJobsOfNonUsers(umi.getUserIds());
952 }
953 }
954
Makoto Onukie7b96182017-08-30 14:53:16 -0700955 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
956 if ("android".equals(pkgName)) {
957 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
958 return;
959 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700960 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700961 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
962 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
963 final JobStatus job = jobsForUid.get(i);
964 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700965 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700966 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700967 }
968 }
969 }
970
Christopher Tate7060b042014-06-09 19:50:00 -0700971 /**
972 * Entry point from client to cancel all jobs originating from their uid.
973 * This will remove the job from the master list, and cancel the job if it was staged for
974 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700975 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800976 *
Christopher Tate7060b042014-06-09 19:50:00 -0700977 */
Christopher Tate8c67d122017-09-29 16:54:26 -0700978 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -0700979 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700980 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -0700981 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -0700982 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700983
984 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800985 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700986 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
987 for (int i=0; i<jobsForUid.size(); i++) {
988 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700989 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -0700990 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700991 }
Christopher Tate7060b042014-06-09 19:50:00 -0700992 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700993 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -0700994 }
995
996 /**
997 * Entry point from client to cancel the job corresponding to the jobId provided.
998 * This will remove the job from the master list, and cancel the job if it was staged for
999 * execution or being executed.
1000 * @param uid Uid of the calling client.
1001 * @param jobId Id of the job, provided at schedule-time.
1002 */
Makoto Onukid2bfec62018-01-12 13:58:01 -08001003 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -07001004 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001005 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001006 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001007 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001008 cancelJobImplLocked(toCancel, null,
1009 "cancel() called by app, callingUid=" + callingUid
1010 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001011 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001012 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001013 }
1014 }
1015
Dianne Hackborn729a3282017-06-09 16:06:01 -07001016 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001017 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1018 cancelled.unprepareLocked(ActivityManager.getService());
1019 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1020 // Remove from pending queue.
1021 if (mPendingJobs.remove(cancelled)) {
1022 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001023 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001024 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001025 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001026 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001027 }
1028
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001029 void updateUidState(int uid, int procState) {
1030 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001031 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1032 // Only use this if we are exactly the top app. All others can live
1033 // with just the foreground priority. This means that persistent processes
1034 // can never be the top app priority... that is fine.
1035 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001036 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001037 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001038 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001039 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001040 }
1041 }
1042 }
1043
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001044 @Override
1045 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001046 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001047 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001048 // When becoming idle, make sure no jobs are actively running,
1049 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001050 for (int i=0; i<mActiveServices.size(); i++) {
1051 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001052 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001053 if (executing != null
1054 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001055 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1056 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001057 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001058 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001059 } else {
1060 // When coming out of idle, allow thing to start back up.
1061 if (mReadyToRock) {
1062 if (mLocalDeviceIdleController != null) {
1063 if (!mReportedActive) {
1064 mReportedActive = true;
1065 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001066 }
1067 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001068 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001069 }
1070 }
1071 }
1072 }
1073
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001074 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001075 // active is true if pending queue contains jobs OR some job is running.
1076 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001077 if (mPendingJobs.size() <= 0) {
1078 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001079 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001080 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001081 if (job != null
1082 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001083 && !job.dozeWhitelisted
1084 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001085 // We will report active if we have a job running and it is not an exception
1086 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001087 active = true;
1088 break;
1089 }
1090 }
1091 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001092
1093 if (mReportedActive != active) {
1094 mReportedActive = active;
1095 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001096 mLocalDeviceIdleController.setJobsActive(active);
1097 }
1098 }
1099 }
1100
Christopher Tated117b292018-01-05 17:32:36 -08001101 void reportAppUsage(String packageName, int userId) {
1102 // This app just transitioned into interactive use or near equivalent, so we should
1103 // take a look at its job state for feedback purposes.
1104 }
1105
Christopher Tate7060b042014-06-09 19:50:00 -07001106 /**
1107 * Initializes the system service.
1108 * <p>
1109 * Subclasses must define a single argument constructor that accepts the context
1110 * and passes it to super.
1111 * </p>
1112 *
1113 * @param context The system server context.
1114 */
1115 public JobSchedulerService(Context context) {
1116 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001117
1118 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001119 mActivityManagerInternal = Preconditions.checkNotNull(
1120 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001121
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001122 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001123 mConstants = new Constants();
1124 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001125 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001126
1127 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001128 mStandbyTracker = new StandbyTracker();
1129 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1130 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001131
1132 // The job store needs to call back
1133 publishLocalService(JobSchedulerInternal.class, new LocalService());
1134
1135 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001136 mJobs = JobStore.initAndGet(this);
1137
Christopher Tate7060b042014-06-09 19:50:00 -07001138 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001139 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001140 mControllers.add(new ConnectivityController(this));
1141 mControllers.add(new TimeController(this));
1142 mControllers.add(new IdleController(this));
1143 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001144 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001145 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001146 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001147 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001148 mControllers.add(new ContentObserverController(this));
1149 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001150 mControllers.add(mDeviceIdleJobsController);
Christopher Tate616541d2017-07-26 14:27:38 -07001151
1152 // If the job store determined that it can't yet reschedule persisted jobs,
1153 // we need to start watching the clock.
1154 if (!mJobs.jobTimesInflatedValid()) {
1155 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1156 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1157 }
Christopher Tate7060b042014-06-09 19:50:00 -07001158 }
1159
Christopher Tate616541d2017-07-26 14:27:38 -07001160 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1161 @Override
1162 public void onReceive(Context context, Intent intent) {
1163 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1164 // When we reach clock sanity, recalculate the temporal windows
1165 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001166 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001167 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1168
1169 // We've done our job now, so stop watching the time.
1170 context.unregisterReceiver(this);
1171
1172 // And kick off the work to update the affected jobs, using a secondary
1173 // thread instead of chugging away here on the main looper thread.
1174 FgThread.getHandler().post(mJobTimeUpdater);
1175 }
1176 }
1177 }
1178 };
1179
1180 private final Runnable mJobTimeUpdater = () -> {
1181 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1182 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1183 synchronized (mLock) {
1184 // Note: we intentionally both look up the existing affected jobs and replace them
1185 // with recalculated ones inside the same lock lifetime.
1186 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1187
1188 // Now, at each position [i], we have both the existing JobStatus
1189 // and the one that replaces it.
1190 final int N = toAdd.size();
1191 for (int i = 0; i < N; i++) {
1192 final JobStatus oldJob = toRemove.get(i);
1193 final JobStatus newJob = toAdd.get(i);
1194 if (DEBUG) {
1195 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1196 }
1197 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1198 }
1199 }
1200 };
1201
Christopher Tate7060b042014-06-09 19:50:00 -07001202 @Override
1203 public void onStart() {
1204 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1205 }
1206
1207 @Override
1208 public void onBootPhase(int phase) {
1209 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001210 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001211
Makoto Onukie4918212018-02-06 11:30:15 -08001212 mAppStateTracker = Preconditions.checkNotNull(
1213 LocalServices.getService(AppStateTracker.class));
Christopher Tated1aebb32018-01-31 13:24:14 -08001214 setNextHeartbeatAlarm();
Makoto Onuki15407842018-01-19 14:23:11 -08001215
Shreyas Basarge5db09082016-01-07 13:38:29 +00001216 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001217 final IntentFilter filter = new IntentFilter();
1218 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1219 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001220 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1221 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001222 filter.addDataScheme("package");
1223 getContext().registerReceiverAsUser(
1224 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1225 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1226 getContext().registerReceiverAsUser(
1227 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001228 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001229 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001230 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001231 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1232 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001233 } catch (RemoteException e) {
1234 // ignored; both services live in system_server
1235 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001236 // Remove any jobs that are not associated with any of the current users.
1237 cancelJobsForNonExistentUsers();
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001238 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001239 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001240 // Let's go!
1241 mReadyToRock = true;
1242 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1243 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001244 mLocalDeviceIdleController
1245 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001246 // Create the "runners".
1247 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1248 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001249 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001250 getContext().getMainLooper()));
1251 }
1252 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001253 mJobs.forEachJob((job) -> {
1254 for (int controller = 0; controller < mControllers.size(); controller++) {
1255 final StateController sc = mControllers.get(controller);
1256 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001257 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001258 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001259 // GO GO GO!
1260 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1261 }
Christopher Tate7060b042014-06-09 19:50:00 -07001262 }
1263 }
1264
1265 /**
1266 * Called when we have a job status object that we need to insert in our
1267 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1268 * about.
1269 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001270 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1271 if (!jobStatus.isPreparedLocked()) {
1272 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1273 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001274 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001275 final boolean update = mJobs.add(jobStatus);
1276 if (mReadyToRock) {
1277 for (int i = 0; i < mControllers.size(); i++) {
1278 StateController controller = mControllers.get(i);
1279 if (update) {
1280 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001281 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001282 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001283 }
Christopher Tate7060b042014-06-09 19:50:00 -07001284 }
1285 }
1286
1287 /**
1288 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1289 * object removed.
1290 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001291 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001292 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001293 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001294 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001295
1296 // Remove from store as well as controllers.
1297 final boolean removed = mJobs.remove(jobStatus, writeBack);
1298 if (removed && mReadyToRock) {
1299 for (int i=0; i<mControllers.size(); i++) {
1300 StateController controller = mControllers.get(i);
1301 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001302 }
1303 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001304 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001305 }
1306
Dianne Hackborn729a3282017-06-09 16:06:01 -07001307 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001308 for (int i=0; i<mActiveServices.size(); i++) {
1309 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001310 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001311 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001312 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001313 return true;
1314 }
1315 }
1316 return false;
1317 }
1318
1319 /**
1320 * @param job JobStatus we are querying against.
1321 * @return Whether or not the job represented by the status object is currently being run or
1322 * is pending.
1323 */
1324 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001325 for (int i=0; i<mActiveServices.size(); i++) {
1326 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001327 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001328 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1329 return true;
1330 }
1331 }
1332 return false;
1333 }
1334
Dianne Hackborn807de782016-04-07 17:54:41 -07001335 void noteJobsPending(List<JobStatus> jobs) {
1336 for (int i = jobs.size() - 1; i >= 0; i--) {
1337 JobStatus job = jobs.get(i);
1338 mJobPackageTracker.notePending(job);
1339 }
1340 }
1341
1342 void noteJobsNonpending(List<JobStatus> jobs) {
1343 for (int i = jobs.size() - 1; i >= 0; i--) {
1344 JobStatus job = jobs.get(i);
1345 mJobPackageTracker.noteNonpending(job);
1346 }
1347 }
1348
Christopher Tate7060b042014-06-09 19:50:00 -07001349 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001350 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1351 * specify an override deadline on a failed job (the failed job will run even though it's not
1352 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1353 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1354 *
Christopher Tate7060b042014-06-09 19:50:00 -07001355 * @param failureToReschedule Provided job status that we will reschedule.
1356 * @return A newly instantiated JobStatus with the same constraints as the last job except
1357 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001358 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001359 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001360 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001361 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001362 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001363 final JobInfo job = failureToReschedule.getJob();
1364
1365 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001366 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1367 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001368
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001369 if (failureToReschedule.hasWorkLocked()) {
1370 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1371 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1372 + backoffAttempts + " > work limit "
1373 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1374 return null;
1375 }
1376 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1377 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1378 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1379 return null;
1380 }
1381
Christopher Tate7060b042014-06-09 19:50:00 -07001382 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001383 case JobInfo.BACKOFF_POLICY_LINEAR: {
1384 long backoff = initialBackoffMillis;
1385 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1386 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1387 }
1388 delayMillis = backoff * backoffAttempts;
1389 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001390 default:
1391 if (DEBUG) {
1392 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1393 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001394 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1395 long backoff = initialBackoffMillis;
1396 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1397 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1398 }
1399 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1400 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001401 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001402 delayMillis =
1403 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001404 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1405 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001406 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001407 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001408 for (int ic=0; ic<mControllers.size(); ic++) {
1409 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001410 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001411 }
1412 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001413 }
1414
1415 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001416 * Called after a periodic has executed so we can reschedule it. We take the last execution
1417 * time of the job to be the time of completion (i.e. the time at which this function is
1418 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001419 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001420 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1421 * to underscheduling at least, rather than if we had taken the last execution time to be the
1422 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001423 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1424 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001425 * @return A new job representing the execution criteria for this instantiation of the
1426 * recurring job.
1427 */
1428 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001429 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001430 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001431 long runEarly = 0L;
1432
1433 // If this periodic was rescheduled it won't have a deadline.
1434 if (periodicToReschedule.hasDeadlineConstraint()) {
1435 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1436 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001437 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001438 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001439 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1440 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001441
1442 if (DEBUG) {
1443 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1444 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1445 }
Christopher Tatea732f012017-10-26 17:26:53 -07001446 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1447 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1448 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001449 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001450 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001451 }
1452
Christopher Tate325768c2018-03-07 16:07:56 -08001453 /*
1454 * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1455 * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1456 * reboot behavior.
1457 */
Christopher Tated1aebb32018-01-31 13:24:14 -08001458 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
Christopher Tate325768c2018-03-07 16:07:56 -08001459 // The furthest back in pre-boot time that we need to bother with
1460 long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1461 boolean cacheHit = false;
Christopher Tated1aebb32018-01-31 13:24:14 -08001462 synchronized (mLock) {
Christopher Tate325768c2018-03-07 16:07:56 -08001463 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1464 if (jobPackages != null) {
1465 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1466 if (cachedValue < Long.MAX_VALUE) {
1467 cacheHit = true;
1468 heartbeat = cachedValue;
1469 }
1470 }
1471 if (!cacheHit) {
1472 // We haven't seen it yet; ask usage stats about it
1473 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1474 if (timeSinceJob < Long.MAX_VALUE) {
1475 // Usage stats knows about it from before, so calculate back from that
1476 // and go from there.
1477 heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1478 }
1479 // If usage stats returned its "not found" MAX_VALUE, we still have the
1480 // negative default 'heartbeat' value we established above
1481 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1482 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001483 }
1484 if (DEBUG_STANDBY) {
Christopher Tate325768c2018-03-07 16:07:56 -08001485 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1486 + packageName + "/" + userId);
Christopher Tated1aebb32018-01-31 13:24:14 -08001487 }
1488 return heartbeat;
1489 }
1490
1491 long heartbeatWhenJobsLastRun(JobStatus job) {
1492 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1493 }
1494
Christopher Tate325768c2018-03-07 16:07:56 -08001495 void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1496 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1497 if (jobPackages == null) {
1498 jobPackages = new HashMap<>();
1499 mLastJobHeartbeats.put(userId, jobPackages);
1500 }
1501 jobPackages.put(packageName, heartbeat);
1502 }
1503
Christopher Tate7060b042014-06-09 19:50:00 -07001504 // JobCompletedListener implementations.
1505
1506 /**
1507 * A job just finished executing. We fetch the
1508 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001509 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001510 * @param jobStatus Completed job.
1511 * @param needsReschedule Whether the implementing class should reschedule this job.
1512 */
1513 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001514 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001515 if (DEBUG) {
1516 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1517 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001518
1519 // If the job wants to be rescheduled, we first need to make the next upcoming
1520 // job so we can transfer any appropriate state over from the previous job when
1521 // we stop it.
1522 final JobStatus rescheduledJob = needsReschedule
1523 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1524
Shreyas Basarge73f10252016-02-11 17:06:13 +00001525 // Do not write back immediately if this is a periodic job. The job may get lost if system
1526 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001527 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001528 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001529 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001530 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001531 // We still want to check for jobs to execute, because this job may have
1532 // scheduled a new job under the same job id, and now we can run it.
1533 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001534 return;
1535 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001536
1537 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001538 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001539 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001540 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001541 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001542 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001543 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001544 } else if (jobStatus.getJob().isPeriodic()) {
1545 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001546 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001547 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001548 } catch (SecurityException e) {
1549 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1550 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001551 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001552 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001553 jobStatus.unprepareLocked(ActivityManager.getService());
1554 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001555 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001556 }
1557
1558 // StateChangedListener implementations.
1559
1560 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001561 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1562 * some controller's state has changed, so as to run through the list of jobs and start/stop
1563 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001564 */
1565 @Override
1566 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001567 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001568 }
1569
1570 @Override
1571 public void onRunJobNow(JobStatus jobStatus) {
1572 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1573 }
1574
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001575 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001576
1577 public JobHandler(Looper looper) {
1578 super(looper);
1579 }
1580
1581 @Override
1582 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001583 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001584 if (!mReadyToRock) {
1585 return;
1586 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001587 switch (message.what) {
1588 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001589 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001590 // runNow can be null, which is a controller's way of indicating that its
1591 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001592 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001593 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001594 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001595 } else {
1596 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001597 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001598 } break;
1599 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001600 if (mReportedActive) {
1601 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001602 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001603 } else {
1604 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001605 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001606 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001607 break;
1608 case MSG_CHECK_JOB_GREEDY:
1609 queueReadyJobsForExecutionLocked();
1610 break;
1611 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001612 cancelJobImplLocked((JobStatus) message.obj, null,
1613 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001614 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001615
1616 case MSG_UID_STATE_CHANGED: {
1617 final int uid = message.arg1;
1618 final int procState = message.arg2;
1619 updateUidState(uid, procState);
1620 break;
1621 }
1622 case MSG_UID_GONE: {
1623 final int uid = message.arg1;
1624 final boolean disabled = message.arg2 != 0;
1625 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1626 if (disabled) {
1627 cancelJobsForUid(uid, "uid gone");
1628 }
1629 synchronized (mLock) {
1630 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1631 }
1632 break;
1633 }
1634 case MSG_UID_ACTIVE: {
1635 final int uid = message.arg1;
1636 synchronized (mLock) {
1637 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1638 }
1639 break;
1640 }
1641 case MSG_UID_IDLE: {
1642 final int uid = message.arg1;
1643 final boolean disabled = message.arg2 != 0;
1644 if (disabled) {
1645 cancelJobsForUid(uid, "app uid idle");
1646 }
1647 synchronized (mLock) {
1648 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1649 }
1650 break;
1651 }
1652
Matthew Williams75fc5252014-09-02 16:17:53 -07001653 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001654 maybeRunPendingJobsLocked();
1655 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1656 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001657 }
1658 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001659 }
Christopher Tate7060b042014-06-09 19:50:00 -07001660
Dianne Hackborn6d068262017-05-16 13:14:37 -07001661 private void stopNonReadyActiveJobsLocked() {
1662 for (int i=0; i<mActiveServices.size(); i++) {
1663 JobServiceContext serviceContext = mActiveServices.get(i);
1664 final JobStatus running = serviceContext.getRunningJobLocked();
1665 if (running != null && !running.isReady()) {
1666 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001667 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1668 "cancelled due to unsatisfied constraints");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001669 }
1670 }
1671 }
1672
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001673 /**
1674 * Run through list of jobs and execute all possible - at least one is expired so we do
1675 * as many as we can.
1676 */
1677 private void queueReadyJobsForExecutionLocked() {
1678 if (DEBUG) {
1679 Slog.d(TAG, "queuing all ready jobs for execution:");
1680 }
1681 noteJobsNonpending(mPendingJobs);
1682 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001683 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001684 mJobs.forEachJob(mReadyQueueFunctor);
1685 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001686
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001687 if (DEBUG) {
1688 final int queuedJobs = mPendingJobs.size();
1689 if (queuedJobs == 0) {
1690 Slog.d(TAG, "No jobs pending.");
1691 } else {
1692 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001693 }
1694 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001695 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001696
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001697 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001698 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001699
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001700 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001701 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001702 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001703 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001704 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001705 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001706 if (newReadyJobs == null) {
1707 newReadyJobs = new ArrayList<JobStatus>();
1708 }
1709 newReadyJobs.add(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001710 }
1711 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001712
1713 public void postProcess() {
1714 if (newReadyJobs != null) {
1715 noteJobsPending(newReadyJobs);
1716 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001717 if (mPendingJobs.size() > 1) {
1718 mPendingJobs.sort(mEnqueueTimeComparator);
1719 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001720 }
1721 newReadyJobs = null;
1722 }
1723 }
1724 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1725
1726 /**
1727 * The state of at least one job has changed. Here is where we could enforce various
1728 * policies on when we want to execute jobs.
1729 * Right now the policy is such:
1730 * If >1 of the ready jobs is idle mode we send all of them off
1731 * if more than 2 network connectivity jobs are ready we send them all off.
1732 * If more than 4 jobs total are ready we send them all off.
1733 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1734 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001735 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001736 int chargingCount;
1737 int batteryNotLowCount;
1738 int storageNotLowCount;
1739 int idleCount;
1740 int backoffCount;
1741 int connectivityCount;
1742 int contentCount;
1743 List<JobStatus> runnableJobs;
1744
1745 public MaybeReadyJobQueueFunctor() {
1746 reset();
1747 }
1748
1749 // Functor method invoked for each job via JobStore.forEachJob()
1750 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001751 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001752 if (isReadyToBeExecutedLocked(job)) {
1753 try {
1754 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1755 job.getJob().getService().getPackageName())) {
1756 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1757 + job.getJob().toString() + " -- package not allowed to start");
1758 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1759 return;
1760 }
1761 } catch (RemoteException e) {
1762 }
1763 if (job.getNumFailures() > 0) {
1764 backoffCount++;
1765 }
1766 if (job.hasIdleConstraint()) {
1767 idleCount++;
1768 }
1769 if (job.hasConnectivityConstraint()) {
1770 connectivityCount++;
1771 }
1772 if (job.hasChargingConstraint()) {
1773 chargingCount++;
1774 }
1775 if (job.hasBatteryNotLowConstraint()) {
1776 batteryNotLowCount++;
1777 }
1778 if (job.hasStorageNotLowConstraint()) {
1779 storageNotLowCount++;
1780 }
1781 if (job.hasContentTriggerConstraint()) {
1782 contentCount++;
1783 }
1784 if (runnableJobs == null) {
1785 runnableJobs = new ArrayList<>();
1786 }
1787 runnableJobs.add(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001788 }
1789 }
1790
1791 public void postProcess() {
1792 if (backoffCount > 0 ||
1793 idleCount >= mConstants.MIN_IDLE_COUNT ||
1794 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1795 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1796 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1797 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1798 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1799 (runnableJobs != null
1800 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1801 if (DEBUG) {
1802 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1803 }
1804 noteJobsPending(runnableJobs);
1805 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001806 if (mPendingJobs.size() > 1) {
1807 mPendingJobs.sort(mEnqueueTimeComparator);
1808 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001809 } else {
1810 if (DEBUG) {
1811 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1812 }
1813 }
1814
1815 // Be ready for next time
1816 reset();
1817 }
1818
1819 private void reset() {
1820 chargingCount = 0;
1821 idleCount = 0;
1822 backoffCount = 0;
1823 connectivityCount = 0;
1824 batteryNotLowCount = 0;
1825 storageNotLowCount = 0;
1826 contentCount = 0;
1827 runnableJobs = null;
1828 }
1829 }
1830 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1831
1832 private void maybeQueueReadyJobsForExecutionLocked() {
1833 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1834
1835 noteJobsNonpending(mPendingJobs);
1836 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001837 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001838 mJobs.forEachJob(mMaybeQueueFunctor);
1839 mMaybeQueueFunctor.postProcess();
1840 }
1841
Christopher Tated1aebb32018-01-31 13:24:14 -08001842 /**
1843 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
1844 */
1845 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1846
1847 @Override
1848 public void onAlarm() {
1849 synchronized (mLock) {
1850 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1851 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1852 if (beatsElapsed > 0) {
1853 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1854 advanceHeartbeatLocked(beatsElapsed);
1855 }
Christopher Tatea732f012017-10-26 17:26:53 -07001856 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001857 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07001858 }
Christopher Tatea732f012017-10-26 17:26:53 -07001859 }
1860
Christopher Tated1aebb32018-01-31 13:24:14 -08001861 // Intentionally does not touch the alarm timing
1862 void advanceHeartbeatLocked(long beatsElapsed) {
1863 mHeartbeat += beatsElapsed;
1864 if (DEBUG_STANDBY) {
1865 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1866 + " to " + mHeartbeat);
1867 }
1868 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
1869 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1870 // new jobs scheduled by apps in that bucket will be permitted to run
1871 // immediately.
1872 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07001873 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001874 // Did we reach or cross a bucket boundary?
1875 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1876 didAdvanceBucket = true;
1877 }
1878 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07001879 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1880 }
1881 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001882 Slog.v(TAG, " Bucket " + i + " next heartbeat "
1883 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07001884 }
1885 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001886
1887 if (didAdvanceBucket) {
1888 if (DEBUG_STANDBY) {
1889 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1890 }
1891 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1892 }
1893 }
1894
1895 void setNextHeartbeatAlarm() {
1896 final long heartbeatLength;
1897 synchronized (mLock) {
1898 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1899 }
1900 final long now = sElapsedRealtimeClock.millis();
1901 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1902 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1903 if (DEBUG_STANDBY) {
1904 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1905 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1906 }
1907 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1908 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1909 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07001910 }
1911
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001912 /**
1913 * Criteria for moving a job into the pending queue:
1914 * - It's ready.
1915 * - It's not pending.
1916 * - It's not already running on a JSC.
1917 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07001918 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001919 * - The component is enabled and runnable.
1920 */
1921 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001922 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001923
1924 if (DEBUG) {
1925 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1926 + " ready=" + jobReady);
1927 }
1928
1929 // This is a condition that is very likely to be false (most jobs that are
1930 // scheduled are sitting there, not ready yet) and very cheap to check (just
1931 // a few conditions on data in JobStatus).
1932 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08001933 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
1934 Slog.v(TAG, " NOT READY: " + job);
1935 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001936 return false;
1937 }
1938
1939 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001940
1941 final int userId = job.getUserId();
1942 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1943
1944 if (DEBUG) {
1945 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001946 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001947 }
1948
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001949 // These are also fairly cheap to check, though they typically will not
1950 // be conditions we fail.
1951 if (!jobExists || !userStarted) {
1952 return false;
1953 }
1954
1955 final boolean jobPending = mPendingJobs.contains(job);
1956 final boolean jobActive = isCurrentlyActiveLocked(job);
1957
1958 if (DEBUG) {
1959 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1960 + " pending=" + jobPending + " active=" + jobActive);
1961 }
1962
1963 // These can be a little more expensive (especially jobActive, since we need to
1964 // go through the array of all potentially active jobs), so we are doing them
1965 // later... but still before checking with the package manager!
1966 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001967 return false;
1968 }
1969
Christopher Tatea732f012017-10-26 17:26:53 -07001970 // If the app is in a non-active standby bucket, make sure we've waited
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001971 // an appropriate amount of time since the last invocation. During device-
1972 // wide parole, standby bucketing is ignored.
Makoto Onuki959acb52018-01-26 14:10:03 -08001973 //
Christopher Tate20afddd2018-02-28 15:19:19 -08001974 // Jobs in 'active' apps are not subject to standby, nor are jobs that are
1975 // specifically marked as exempt.
1976 if (DEBUG_STANDBY) {
1977 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1978 + " parole=" + mInParole + " active=" + job.uidActive
1979 + " exempt=" + job.getJob().isExemptedFromAppStandby());
1980 }
1981 if (!mInParole
1982 && !job.uidActive
1983 && !job.getJob().isExemptedFromAppStandby()) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001984 final int bucket = job.getStandbyBucket();
Christopher Tate20afddd2018-02-28 15:19:19 -08001985 if (DEBUG_STANDBY) {
1986 Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
1987 + " next=" + mNextBucketHeartbeat[bucket]);
1988 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001989 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001990 // Only skip this job if the app is still waiting for the end of its nominal
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001991 // bucket interval. Once it's waited that long, we let it go ahead and clear.
1992 // The final (NEVER) bucket is special; we never age those apps' jobs into
1993 // runnability.
Christopher Tated1aebb32018-01-31 13:24:14 -08001994 final long appLastRan = heartbeatWhenJobsLastRun(job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001995 if (bucket >= mConstants.STANDBY_BEATS.length
Christopher Tated1aebb32018-01-31 13:24:14 -08001996 || (mHeartbeat > appLastRan
1997 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001998 // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1999 if (job.getWhenStandbyDeferred() == 0) {
2000 if (DEBUG_STANDBY) {
2001 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
Christopher Tated1aebb32018-01-31 13:24:14 -08002002 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2003 + " for " + job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002004 }
2005 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tate0c4d7682017-12-06 15:10:22 -08002006 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002007 return false;
2008 } else {
2009 if (DEBUG_STANDBY) {
2010 Slog.v(TAG, "Bucket deferred job aged into runnability at "
2011 + mHeartbeat + " : " + job);
2012 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08002013 }
Christopher Tatea732f012017-10-26 17:26:53 -07002014 }
Christopher Tatea732f012017-10-26 17:26:53 -07002015 }
2016
2017 // The expensive check last: validate that the defined package+service is
2018 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002019 final boolean componentPresent;
2020 try {
2021 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2022 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2023 userId) != null);
2024 } catch (RemoteException e) {
2025 throw e.rethrowAsRuntimeException();
2026 }
2027
2028 if (DEBUG) {
2029 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2030 + " componentPresent=" + componentPresent);
2031 }
2032
2033 // Everything else checked out so far, so this is the final yes/no check
2034 return componentPresent;
2035 }
2036
2037 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002038 * Reconcile jobs in the pending queue against available execution contexts.
2039 * A controller can force a job into the pending queue even if it's already running, but
2040 * here is where we decide whether to actually execute it.
2041 */
2042 private void maybeRunPendingJobsLocked() {
2043 if (DEBUG) {
2044 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2045 }
2046 assignJobsToContextsLocked();
2047 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002048 }
2049
Dianne Hackborn807de782016-04-07 17:54:41 -07002050 private int adjustJobPriority(int curPriority, JobStatus job) {
2051 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2052 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002053 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002054 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002055 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002056 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2057 }
2058 }
2059 return curPriority;
2060 }
2061
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002062 private int evaluateJobPriorityLocked(JobStatus job) {
2063 int priority = job.getPriority();
2064 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002065 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002066 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002067 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2068 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002069 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002070 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002071 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002072 }
2073
Christopher Tate7060b042014-06-09 19:50:00 -07002074 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00002075 * Takes jobs from pending queue and runs them on available contexts.
2076 * If no contexts are available, preempts lower priority jobs to
2077 * run higher priority ones.
2078 * Lock on mJobs before calling this function.
2079 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08002080 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002081 if (DEBUG) {
2082 Slog.d(TAG, printPendingQueue());
2083 }
2084
Dianne Hackborn970510b2016-02-24 16:56:42 -08002085 int memLevel;
2086 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08002087 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002088 } catch (RemoteException e) {
2089 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
2090 }
2091 switch (memLevel) {
2092 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002093 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002094 break;
2095 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002096 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002097 break;
2098 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002099 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002100 break;
2101 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002102 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002103 break;
2104 }
2105
2106 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
2107 boolean[] act = mTmpAssignAct;
2108 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2109 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002110 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002111 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2112 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002113 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002114 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002115 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002116 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2117 numForeground++;
2118 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002119 }
2120 act[i] = false;
2121 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002122 }
2123 if (DEBUG) {
2124 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2125 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002126 for (int i=0; i<mPendingJobs.size(); i++) {
2127 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002128
2129 // If job is already running, go to next job.
2130 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2131 if (jobRunningContext != -1) {
2132 continue;
2133 }
2134
Dianne Hackborn970510b2016-02-24 16:56:42 -08002135 final int priority = evaluateJobPriorityLocked(nextPending);
2136 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002137
Shreyas Basarge5db09082016-01-07 13:38:29 +00002138 // Find a context for nextPending. The context should be available OR
2139 // it should have lowest priority among all running jobs
2140 // (sharing the same Uid as nextPending)
2141 int minPriority = Integer.MAX_VALUE;
2142 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002143 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2144 JobStatus job = contextIdToJobMap[j];
2145 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00002146 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002147 if ((numActive < mMaxActiveJobs ||
2148 (priority >= JobInfo.PRIORITY_TOP_APP &&
2149 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08002150 (preferredUid == nextPending.getUid() ||
2151 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2152 // This slot is free, and we haven't yet hit the limit on
2153 // concurrent jobs... we can just throw the job in to here.
2154 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002155 break;
2156 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00002157 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08002158 // the context has a preferred Uid or we have reached the limit on
2159 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00002160 continue;
2161 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002162 if (job.getUid() != nextPending.getUid()) {
2163 continue;
2164 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002165 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002166 continue;
2167 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002168 if (minPriority > nextPending.lastEvaluatedPriority) {
2169 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002170 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00002171 }
2172 }
2173 if (minPriorityContextId != -1) {
2174 contextIdToJobMap[minPriorityContextId] = nextPending;
2175 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002176 numActive++;
2177 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2178 numForeground++;
2179 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002180 }
2181 }
2182 if (DEBUG) {
2183 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2184 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002185 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002186 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002187 boolean preservePreferredUid = false;
2188 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002189 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002190 if (js != null) {
2191 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002192 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00002193 }
2194 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07002195 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002196 preservePreferredUid = true;
2197 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002198 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00002199 if (DEBUG) {
2200 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002201 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002202 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002203 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002204 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002205 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002206 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2207 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002208 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002209 if (mPendingJobs.remove(pendingJob)) {
2210 mJobPackageTracker.noteNonpending(pendingJob);
2211 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002212 }
2213 }
2214 if (!preservePreferredUid) {
2215 mActiveServices.get(i).clearPreferredUid();
2216 }
2217 }
2218 }
2219
2220 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2221 for (int i=0; i<map.length; i++) {
2222 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2223 return i;
2224 }
2225 }
2226 return -1;
2227 }
2228
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002229 final class LocalService implements JobSchedulerInternal {
2230
2231 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002232 * The current bucket heartbeat ordinal
2233 */
2234 public long currentHeartbeat() {
2235 return getCurrentHeartbeat();
2236 }
2237
2238 /**
2239 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2240 */
2241 public long nextHeartbeatForBucket(int bucket) {
2242 synchronized (mLock) {
2243 return mNextBucketHeartbeat[bucket];
2244 }
2245 }
2246
2247 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002248 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2249 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2250 * jobs in a long time is immediately runnable even if the app is bucketed into
2251 * an infrequent time allocation.
2252 */
2253 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2254 final int appStandbyBucket) {
2255 if (appStandbyBucket == 0 ||
2256 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2257 // ACTIVE => everything can be run right away
2258 // NEVER => we won't run them anyway, so let them go in the future
2259 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002260 if (DEBUG_STANDBY) {
2261 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2262 + packageName + "/" + userId);
2263 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002264 return 0;
2265 }
2266
Christopher Tated1aebb32018-01-31 13:24:14 -08002267 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2268 if (DEBUG_STANDBY) {
2269 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2270 + packageName + "/" + userId);
2271 }
2272 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002273 }
2274
Christopher Tate325768c2018-03-07 16:07:56 -08002275 public void noteJobStart(String packageName, int userId) {
2276 synchronized (mLock) {
2277 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2278 }
2279 }
2280
Christopher Tate435c2f42018-01-18 12:59:15 -08002281 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002282 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2283 * jobs are always considered pending.
2284 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002285 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002286 public List<JobInfo> getSystemScheduledPendingJobs() {
2287 synchronized (mLock) {
2288 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002289 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2290 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2291 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002292 }
2293 });
2294 return pendingJobs;
2295 }
2296 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002297
2298 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002299 public void cancelJobsForUid(int uid, String reason) {
2300 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2301 }
2302
2303 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002304 public void addBackingUpUid(int uid) {
2305 synchronized (mLock) {
2306 // No need to actually do anything here, since for a full backup the
2307 // activity manager will kill the process which will kill the job (and
2308 // cause it to restart, but now it can't run).
2309 mBackingUpUids.put(uid, uid);
2310 }
2311 }
2312
2313 @Override
2314 public void removeBackingUpUid(int uid) {
2315 synchronized (mLock) {
2316 mBackingUpUids.delete(uid);
2317 // If there are any jobs for this uid, we need to rebuild the pending list
2318 // in case they are now ready to run.
2319 if (mJobs.countJobsForUid(uid) > 0) {
2320 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2321 }
2322 }
2323 }
2324
2325 @Override
2326 public void clearAllBackingUpUids() {
2327 synchronized (mLock) {
2328 if (mBackingUpUids.size() > 0) {
2329 mBackingUpUids.clear();
2330 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2331 }
2332 }
2333 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002334
2335 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002336 public void reportAppUsage(String packageName, int userId) {
2337 JobSchedulerService.this.reportAppUsage(packageName, userId);
2338 }
2339
2340 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002341 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002342 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002343 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002344 }
2345 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002346 }
2347
Shreyas Basarge5db09082016-01-07 13:38:29 +00002348 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002349 * Tracking of app assignments to standby buckets
2350 */
2351 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002352
Christopher Tatea732f012017-10-26 17:26:53 -07002353 // AppIdleStateChangeListener interface for live updates
2354
2355 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002356 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002357 boolean idle, int bucket, int reason) {
Christopher Tatea732f012017-10-26 17:26:53 -07002358 final int uid = mLocalPM.getPackageUid(packageName,
2359 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2360 if (uid < 0) {
2361 if (DEBUG_STANDBY) {
2362 Slog.i(TAG, "App idle state change for unknown app "
2363 + packageName + "/" + userId);
2364 }
2365 return;
2366 }
2367
2368 final int bucketIndex = standbyBucketToBucketIndex(bucket);
2369 // update job bookkeeping out of band
2370 BackgroundThread.getHandler().post(() -> {
2371 if (DEBUG_STANDBY) {
2372 Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2373 }
2374 synchronized (mLock) {
Christopher Tate93defea2018-02-20 15:57:17 -08002375 mJobs.forEachJobForSourceUid(uid, job -> {
2376 // double-check uid vs package name to disambiguate shared uids
2377 if (packageName.equals(job.getSourcePackageName())) {
2378 job.setStandbyBucket(bucketIndex);
2379 }
2380 });
Christopher Tatea732f012017-10-26 17:26:53 -07002381 onControllerStateChanged();
2382 }
2383 });
2384 }
2385
2386 @Override
2387 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002388 if (DEBUG_STANDBY) {
2389 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2390 }
2391 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002392 }
Christopher Tated117b292018-01-05 17:32:36 -08002393
2394 @Override
2395 public void onUserInteractionStarted(String packageName, int userId) {
2396 final int uid = mLocalPM.getPackageUid(packageName,
2397 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2398 if (uid < 0) {
2399 // Quietly ignore; the case is already logged elsewhere
2400 return;
2401 }
2402
Amith Yamasani977e11f2018-02-16 11:29:54 -08002403 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2404 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2405 // Too long ago, not worth logging
2406 sinceLast = 0L;
2407 }
Christopher Tated117b292018-01-05 17:32:36 -08002408 final DeferredJobCounter counter = new DeferredJobCounter();
2409 synchronized (mLock) {
2410 mJobs.forEachJobForSourceUid(uid, counter);
2411 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002412 if (counter.numDeferred() > 0 || sinceLast > 0) {
2413 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2414 (BatteryStatsInternal.class);
2415 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2416 }
Christopher Tated117b292018-01-05 17:32:36 -08002417 }
2418 }
2419
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002420 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002421 private int mDeferred = 0;
2422
2423 public int numDeferred() {
2424 return mDeferred;
2425 }
2426
2427 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002428 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002429 if (job.getWhenStandbyDeferred() > 0) {
2430 mDeferred++;
2431 }
2432 }
Christopher Tatea732f012017-10-26 17:26:53 -07002433 }
2434
2435 public static int standbyBucketToBucketIndex(int bucket) {
2436 // Normalize AppStandby constants to indices into our bookkeeping
Christopher Tatef2159712018-03-27 16:04:14 -07002437 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2438 else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2439 else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2440 else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2441 else return ACTIVE_INDEX;
Christopher Tatea732f012017-10-26 17:26:53 -07002442 }
2443
Christopher Tated1aebb32018-01-31 13:24:14 -08002444 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002445 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2446 UsageStatsManagerInternal usageStats = LocalServices.getService(
2447 UsageStatsManagerInternal.class);
2448 int bucket = usageStats != null
2449 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2450 : 0;
2451
2452 bucket = standbyBucketToBucketIndex(bucket);
2453
2454 if (DEBUG_STANDBY) {
2455 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2456 }
2457 return bucket;
2458 }
2459
2460 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002461 * Binder stub trampoline implementation
2462 */
2463 final class JobSchedulerStub extends IJobScheduler.Stub {
2464 /** Cache determination of whether a given app can persist jobs
2465 * key is uid of the calling app; value is undetermined/true/false
2466 */
2467 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2468
2469 // Enforce that only the app itself (or shared uid participant) can schedule a
2470 // job that runs one of the app's services, as well as verifying that the
2471 // named service properly requires the BIND_JOB_SERVICE permission
2472 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002473 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002474 final ComponentName service = job.getService();
2475 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002476 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002477 PackageManager.MATCH_DIRECT_BOOT_AWARE
2478 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002479 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002480 if (si == null) {
2481 throw new IllegalArgumentException("No such service " + service);
2482 }
Christopher Tate7060b042014-06-09 19:50:00 -07002483 if (si.applicationInfo.uid != uid) {
2484 throw new IllegalArgumentException("uid " + uid +
2485 " cannot schedule job in " + service.getPackageName());
2486 }
2487 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2488 throw new IllegalArgumentException("Scheduled service " + service
2489 + " does not require android.permission.BIND_JOB_SERVICE permission");
2490 }
Christopher Tate5568f542014-06-18 13:53:31 -07002491 } catch (RemoteException e) {
2492 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002493 }
2494 }
2495
2496 private boolean canPersistJobs(int pid, int uid) {
2497 // If we get this far we're good to go; all we need to do now is check
2498 // whether the app is allowed to persist its scheduled work.
2499 final boolean canPersist;
2500 synchronized (mPersistCache) {
2501 Boolean cached = mPersistCache.get(uid);
2502 if (cached != null) {
2503 canPersist = cached.booleanValue();
2504 } else {
2505 // Persisting jobs is tantamount to running at boot, so we permit
2506 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2507 // permission
2508 int result = getContext().checkPermission(
2509 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2510 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2511 mPersistCache.put(uid, canPersist);
2512 }
2513 }
2514 return canPersist;
2515 }
2516
Makoto Onuki959acb52018-01-26 14:10:03 -08002517 private void validateJobFlags(JobInfo job, int callingUid) {
2518 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2519 getContext().enforceCallingOrSelfPermission(
2520 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2521 }
2522 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2523 if (callingUid != Process.SYSTEM_UID) {
2524 throw new SecurityException("Job has invalid flags");
2525 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002526 if (job.isPeriodic()) {
2527 Slog.wtf(TAG, "Periodic jobs mustn't have"
2528 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002529 }
2530 }
2531 }
2532
Christopher Tate7060b042014-06-09 19:50:00 -07002533 // IJobScheduler implementation
2534 @Override
2535 public int schedule(JobInfo job) throws RemoteException {
2536 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002537 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002538 }
2539 final int pid = Binder.getCallingPid();
2540 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002541 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002542
2543 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002544 if (job.isPersisted()) {
2545 if (!canPersistJobs(pid, uid)) {
2546 throw new IllegalArgumentException("Error: requested job be persisted without"
2547 + " holding RECEIVE_BOOT_COMPLETED permission.");
2548 }
2549 }
Christopher Tate7060b042014-06-09 19:50:00 -07002550
Makoto Onuki959acb52018-01-26 14:10:03 -08002551 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002552
Christopher Tate7060b042014-06-09 19:50:00 -07002553 long ident = Binder.clearCallingIdentity();
2554 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002555 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2556 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002557 } finally {
2558 Binder.restoreCallingIdentity(ident);
2559 }
2560 }
2561
2562 // IJobScheduler implementation
2563 @Override
2564 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2565 if (DEBUG) {
2566 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2567 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002568 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002569 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002570
2571 enforceValidJobRequest(uid, job);
2572 if (job.isPersisted()) {
2573 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2574 }
2575 if (work == null) {
2576 throw new NullPointerException("work is null");
2577 }
2578
Makoto Onuki959acb52018-01-26 14:10:03 -08002579 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002580
2581 long ident = Binder.clearCallingIdentity();
2582 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002583 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2584 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002585 } finally {
2586 Binder.restoreCallingIdentity(ident);
2587 }
2588 }
2589
2590 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002591 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002592 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002593 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002594 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002595 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002596 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002597 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002598
2599 if (packageName == null) {
2600 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002601 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002602
2603 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2604 android.Manifest.permission.UPDATE_DEVICE_STATS);
2605 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2606 throw new SecurityException("Caller uid " + callerUid
2607 + " not permitted to schedule jobs for other apps");
2608 }
2609
Makoto Onuki959acb52018-01-26 14:10:03 -08002610 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002611
Shreyas Basarge968ac752016-01-11 23:09:26 +00002612 long ident = Binder.clearCallingIdentity();
2613 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002614 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002615 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002616 } finally {
2617 Binder.restoreCallingIdentity(ident);
2618 }
2619 }
2620
2621 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002622 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2623 final int uid = Binder.getCallingUid();
2624
2625 long ident = Binder.clearCallingIdentity();
2626 try {
2627 return JobSchedulerService.this.getPendingJobs(uid);
2628 } finally {
2629 Binder.restoreCallingIdentity(ident);
2630 }
2631 }
2632
2633 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002634 public JobInfo getPendingJob(int jobId) throws RemoteException {
2635 final int uid = Binder.getCallingUid();
2636
2637 long ident = Binder.clearCallingIdentity();
2638 try {
2639 return JobSchedulerService.this.getPendingJob(uid, jobId);
2640 } finally {
2641 Binder.restoreCallingIdentity(ident);
2642 }
2643 }
2644
2645 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002646 public void cancelAll() throws RemoteException {
2647 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002648 long ident = Binder.clearCallingIdentity();
2649 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002650 JobSchedulerService.this.cancelJobsForUid(uid,
2651 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002652 } finally {
2653 Binder.restoreCallingIdentity(ident);
2654 }
2655 }
2656
2657 @Override
2658 public void cancel(int jobId) throws RemoteException {
2659 final int uid = Binder.getCallingUid();
2660
2661 long ident = Binder.clearCallingIdentity();
2662 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002663 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002664 } finally {
2665 Binder.restoreCallingIdentity(ident);
2666 }
2667 }
2668
2669 /**
2670 * "dumpsys" infrastructure
2671 */
2672 @Override
2673 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002674 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002675
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002676 int filterUid = -1;
2677 boolean proto = false;
2678 if (!ArrayUtils.isEmpty(args)) {
2679 int opti = 0;
2680 while (opti < args.length) {
2681 String arg = args[opti];
2682 if ("-h".equals(arg)) {
2683 dumpHelp(pw);
2684 return;
2685 } else if ("-a".equals(arg)) {
2686 // Ignore, we always dump all.
2687 } else if ("--proto".equals(arg)) {
2688 proto = true;
2689 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2690 pw.println("Unknown option: " + arg);
2691 return;
2692 } else {
2693 break;
2694 }
2695 opti++;
2696 }
2697 if (opti < args.length) {
2698 String pkg = args[opti];
2699 try {
2700 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2701 PackageManager.MATCH_ANY_USER);
2702 } catch (NameNotFoundException ignored) {
2703 pw.println("Invalid package: " + pkg);
2704 return;
2705 }
2706 }
2707 }
2708
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002709 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07002710 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002711 if (proto) {
2712 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2713 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002714 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
2715 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002716 }
Christopher Tate7060b042014-06-09 19:50:00 -07002717 } finally {
2718 Binder.restoreCallingIdentity(identityToken);
2719 }
2720 }
Christopher Tate5d346052016-03-08 12:56:08 -08002721
2722 @Override
2723 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002724 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002725 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002726 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002727 }
Serik Beketayev75915d12018-08-01 16:56:59 -07002728
2729 /**
2730 * <b>For internal system user only!</b>
2731 * Returns a list of all currently-executing jobs.
2732 */
2733 @Override
2734 public List<JobInfo> getStartedJobs() {
2735 final int uid = Binder.getCallingUid();
2736 if (uid != Process.SYSTEM_UID) {
2737 throw new SecurityException(
2738 "getStartedJobs() is system internal use only.");
2739 }
2740
2741 final ArrayList<JobInfo> runningJobs;
2742
2743 synchronized (mLock) {
2744 runningJobs = new ArrayList<>(mActiveServices.size());
2745 for (JobServiceContext jsc : mActiveServices) {
2746 final JobStatus job = jsc.getRunningJobLocked();
2747 if (job != null) {
2748 runningJobs.add(job.getJob());
2749 }
2750 }
2751 }
2752
2753 return runningJobs;
2754 }
2755
2756 /**
2757 * <b>For internal system user only!</b>
2758 * Returns a snapshot of the state of all jobs known to the system.
2759 *
2760 * <p class="note">This is a slow operation, so it should be called sparingly.
2761 */
2762 @Override
2763 public List<JobSnapshot> getAllJobSnapshots() {
2764 final int uid = Binder.getCallingUid();
2765 if (uid != Process.SYSTEM_UID) {
2766 throw new SecurityException(
2767 "getAllJobSnapshots() is system internal use only.");
2768 }
2769 synchronized (mLock) {
2770 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
2771 mJobs.forEachJob((job) -> snapshots.add(
2772 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
2773 isReadyToBeExecutedLocked(job))));
2774 return snapshots;
2775 }
2776 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002777 };
2778
Christopher Tate5d346052016-03-08 12:56:08 -08002779 // Shell command infrastructure: run the given job immediately
2780 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2781 if (DEBUG) {
2782 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2783 + " " + jobId + " f=" + force);
2784 }
2785
2786 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002787 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2788 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002789 if (uid < 0) {
2790 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2791 }
2792
2793 synchronized (mLock) {
2794 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2795 if (js == null) {
2796 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2797 }
2798
2799 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2800 if (!js.isConstraintsSatisfied()) {
2801 js.overrideState = 0;
2802 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2803 }
2804
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002805 queueReadyJobsForExecutionLocked();
2806 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002807 }
2808 } catch (RemoteException e) {
2809 // can't happen
2810 }
2811 return 0;
2812 }
2813
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002814 // Shell command infrastructure: immediately timeout currently executing jobs
2815 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2816 boolean hasJobId, int jobId) {
2817 if (DEBUG) {
2818 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2819 }
2820
2821 synchronized (mLock) {
2822 boolean foundSome = false;
2823 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002824 final JobServiceContext jc = mActiveServices.get(i);
2825 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002826 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002827 foundSome = true;
2828 pw.print("Timing out: ");
2829 js.printUniqueId(pw);
2830 pw.print(" ");
2831 pw.println(js.getServiceComponent().flattenToShortString());
2832 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002833 }
2834 if (!foundSome) {
2835 pw.println("No matching executing jobs found.");
2836 }
2837 }
2838 return 0;
2839 }
2840
Christopher Tate8c67d122017-09-29 16:54:26 -07002841 // Shell command infrastructure: cancel a scheduled job
2842 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2843 boolean hasJobId, int jobId) {
2844 if (DEBUG) {
2845 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2846 }
2847
2848 int pkgUid = -1;
2849 try {
2850 IPackageManager pm = AppGlobals.getPackageManager();
2851 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2852 } catch (RemoteException e) { /* can't happen */ }
2853
2854 if (pkgUid < 0) {
2855 pw.println("Package " + pkgName + " not found.");
2856 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2857 }
2858
2859 if (!hasJobId) {
2860 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2861 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2862 pw.println("No matching jobs found.");
2863 }
2864 } else {
2865 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002866 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002867 pw.println("No matching job found.");
2868 }
2869 }
2870
2871 return 0;
2872 }
2873
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002874 void setMonitorBattery(boolean enabled) {
2875 synchronized (mLock) {
2876 if (mBatteryController != null) {
2877 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2878 }
2879 }
2880 }
2881
2882 int getBatterySeq() {
2883 synchronized (mLock) {
2884 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2885 }
2886 }
2887
2888 boolean getBatteryCharging() {
2889 synchronized (mLock) {
2890 return mBatteryController != null
2891 ? mBatteryController.getTracker().isOnStablePower() : false;
2892 }
2893 }
2894
2895 boolean getBatteryNotLow() {
2896 synchronized (mLock) {
2897 return mBatteryController != null
2898 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2899 }
2900 }
2901
Dianne Hackborn532ea262017-03-17 17:50:55 -07002902 int getStorageSeq() {
2903 synchronized (mLock) {
2904 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2905 }
2906 }
2907
2908 boolean getStorageNotLow() {
2909 synchronized (mLock) {
2910 return mStorageController != null
2911 ? mStorageController.getTracker().isStorageNotLow() : false;
2912 }
2913 }
2914
Christopher Tatea732f012017-10-26 17:26:53 -07002915 long getCurrentHeartbeat() {
2916 synchronized (mLock) {
2917 return mHeartbeat;
2918 }
2919 }
2920
Christopher Tated1aebb32018-01-31 13:24:14 -08002921 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07002922 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2923 try {
2924 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2925 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2926 if (uid < 0) {
2927 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2928 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2929 }
2930
2931 synchronized (mLock) {
2932 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2933 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2934 if (js == null) {
2935 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2936 pw.print("/jid"); pw.print(jobId); pw.println(")");
2937 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2938 }
2939
2940 boolean printed = false;
2941 if (mPendingJobs.contains(js)) {
2942 pw.print("pending");
2943 printed = true;
2944 }
2945 if (isCurrentlyActiveLocked(js)) {
2946 if (printed) {
2947 pw.print(" ");
2948 }
2949 printed = true;
2950 pw.println("active");
2951 }
2952 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2953 if (printed) {
2954 pw.print(" ");
2955 }
2956 printed = true;
2957 pw.println("user-stopped");
2958 }
2959 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2960 if (printed) {
2961 pw.print(" ");
2962 }
2963 printed = true;
2964 pw.println("backing-up");
2965 }
2966 boolean componentPresent = false;
2967 try {
2968 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2969 js.getServiceComponent(),
2970 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2971 js.getUserId()) != null);
2972 } catch (RemoteException e) {
2973 }
2974 if (!componentPresent) {
2975 if (printed) {
2976 pw.print(" ");
2977 }
2978 printed = true;
2979 pw.println("no-component");
2980 }
2981 if (js.isReady()) {
2982 if (printed) {
2983 pw.print(" ");
2984 }
2985 printed = true;
2986 pw.println("ready");
2987 }
2988 if (!printed) {
2989 pw.print("waiting");
2990 }
2991 pw.println();
2992 }
2993 } catch (RemoteException e) {
2994 // can't happen
2995 }
2996 return 0;
2997 }
2998
Christopher Tated1aebb32018-01-31 13:24:14 -08002999 // Shell command infrastructure
3000 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
3001 if (numBeats < 1) {
3002 pw.println(getCurrentHeartbeat());
3003 return 0;
3004 }
3005
3006 pw.print("Advancing standby heartbeat by ");
3007 pw.println(numBeats);
3008 synchronized (mLock) {
3009 advanceHeartbeatLocked(numBeats);
3010 }
3011 return 0;
3012 }
3013
lpeter318abc92018-05-04 16:13:14 +08003014 void triggerDockState(boolean idleState) {
3015 final Intent dockIntent;
3016 if (idleState) {
3017 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3018 } else {
3019 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3020 }
3021 dockIntent.setPackage("android");
3022 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3023 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3024 }
3025
Shreyas Basarge5db09082016-01-07 13:38:29 +00003026 private String printContextIdToJobMap(JobStatus[] map, String initial) {
3027 StringBuilder s = new StringBuilder(initial + ": ");
3028 for (int i=0; i<map.length; i++) {
3029 s.append("(")
3030 .append(map[i] == null? -1: map[i].getJobId())
3031 .append(map[i] == null? -1: map[i].getUid())
3032 .append(")" );
3033 }
3034 return s.toString();
3035 }
3036
3037 private String printPendingQueue() {
3038 StringBuilder s = new StringBuilder("Pending queue: ");
3039 Iterator<JobStatus> it = mPendingJobs.iterator();
3040 while (it.hasNext()) {
3041 JobStatus js = it.next();
3042 s.append("(")
3043 .append(js.getJob().getId())
3044 .append(", ")
3045 .append(js.getUid())
3046 .append(") ");
3047 }
3048 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07003049 }
Christopher Tate7060b042014-06-09 19:50:00 -07003050
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003051 static void dumpHelp(PrintWriter pw) {
3052 pw.println("Job Scheduler (jobscheduler) dump options:");
3053 pw.println(" [-h] [package] ...");
3054 pw.println(" -h: print this help");
3055 pw.println(" [package] is an optional package name to limit the output to.");
3056 }
3057
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003058 /** Sort jobs by caller UID, then by Job ID. */
3059 private static void sortJobs(List<JobStatus> jobs) {
3060 Collections.sort(jobs, new Comparator<JobStatus>() {
3061 @Override
3062 public int compare(JobStatus o1, JobStatus o2) {
3063 int uid1 = o1.getUid();
3064 int uid2 = o2.getUid();
3065 int id1 = o1.getJobId();
3066 int id2 = o2.getJobId();
3067 if (uid1 != uid2) {
3068 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003069 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003070 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003071 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003072 });
3073 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003074
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003075 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003076 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003077 final long nowElapsed = sElapsedRealtimeClock.millis();
3078 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003079 final Predicate<JobStatus> predicate = (js) -> {
3080 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3081 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3082 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003083 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003084 mConstants.dump(pw);
3085 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003086
3087 pw.println(" Heartbeat:");
3088 pw.print(" Current: "); pw.println(mHeartbeat);
3089 pw.println(" Next");
3090 pw.print(" ACTIVE: "); pw.println(mNextBucketHeartbeat[0]);
3091 pw.print(" WORKING: "); pw.println(mNextBucketHeartbeat[1]);
3092 pw.print(" FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3093 pw.print(" RARE: "); pw.println(mNextBucketHeartbeat[3]);
3094 pw.print(" Last heartbeat: ");
3095 TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3096 pw.println();
3097 pw.print(" Next heartbeat: ");
3098 TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3099 nowElapsed, pw);
3100 pw.println();
Makoto Onuki0525b982018-05-02 14:46:59 -07003101 pw.print(" In parole?: ");
3102 pw.print(mInParole);
3103 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003104 pw.println();
3105
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003106 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003107 pw.print("Registered ");
3108 pw.print(mJobs.size());
3109 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003110 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003111 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003112 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003113 for (JobStatus job : jobs) {
3114 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3115 pw.println(job.toShortStringExceptUniqueId());
3116
3117 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003118 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003119 continue;
3120 }
3121
Dianne Hackborn6d068262017-05-16 13:14:37 -07003122 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003123 pw.print(" Last run heartbeat: ");
3124 pw.print(heartbeatWhenJobsLastRun(job));
3125 pw.println();
3126
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003127 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003128 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003129 pw.print(" (job=");
3130 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003131 pw.print(" user=");
3132 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003133 pw.print(" !pending=");
3134 pw.print(!mPendingJobs.contains(job));
3135 pw.print(" !active=");
3136 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003137 pw.print(" !backingup=");
3138 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003139 pw.print(" comp=");
3140 boolean componentPresent = false;
3141 try {
3142 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3143 job.getServiceComponent(),
3144 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3145 job.getUserId()) != null);
3146 } catch (RemoteException e) {
3147 }
3148 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003149 pw.println(")");
3150 }
Christopher Tate7060b042014-06-09 19:50:00 -07003151 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003152 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003153 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003154 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003155 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003156 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3157 pw.increaseIndent();
3158 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3159 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003160 }
3161 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003162 pw.println("Uid priority overrides:");
3163 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003164 int uid = mUidPriorityOverride.keyAt(i);
3165 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3166 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3167 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3168 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003169 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003170 if (mBackingUpUids.size() > 0) {
3171 pw.println();
3172 pw.println("Backing up uids:");
3173 boolean first = true;
3174 for (int i = 0; i < mBackingUpUids.size(); i++) {
3175 int uid = mBackingUpUids.keyAt(i);
3176 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3177 if (first) {
3178 pw.print(" ");
3179 first = false;
3180 } else {
3181 pw.print(", ");
3182 }
3183 pw.print(UserHandle.formatUid(uid));
3184 }
3185 }
3186 pw.println();
3187 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003188 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003189 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003190 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003191 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3192 pw.println();
3193 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003194 pw.println("Pending queue:");
3195 for (int i=0; i<mPendingJobs.size(); i++) {
3196 JobStatus job = mPendingJobs.get(i);
3197 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3198 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003199 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003200 int priority = evaluateJobPriorityLocked(job);
3201 if (priority != JobInfo.PRIORITY_DEFAULT) {
3202 pw.print(" Evaluated priority: "); pw.println(priority);
3203 }
3204 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003205 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003206 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003207 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003208 }
Christopher Tate7060b042014-06-09 19:50:00 -07003209 pw.println();
3210 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003211 for (int i=0; i<mActiveServices.size(); i++) {
3212 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003213 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003214 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003215 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003216 if (jsc.mStoppedReason != null) {
3217 pw.print("inactive since ");
3218 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3219 pw.print(", stopped because: ");
3220 pw.println(jsc.mStoppedReason);
3221 } else {
3222 pw.println("inactive");
3223 }
Christopher Tate7060b042014-06-09 19:50:00 -07003224 continue;
3225 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003226 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003227 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003228 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003229 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003230 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003231 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003232 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003233 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003234 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08003235 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003236 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003237 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003238 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003239 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003240 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3241 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003242 }
3243 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003244 if (filterUid == -1) {
3245 pw.println();
3246 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3247 pw.print("mReportedActive="); pw.println(mReportedActive);
3248 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3249 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003250 pw.println();
3251 pw.print("PersistStats: ");
3252 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003253 }
3254 pw.println();
3255 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003256
3257 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3258 ProtoOutputStream proto = new ProtoOutputStream(fd);
3259 final int filterUidFinal = UserHandle.getAppId(filterUid);
3260 final long nowElapsed = sElapsedRealtimeClock.millis();
3261 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003262 final Predicate<JobStatus> predicate = (js) -> {
3263 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3264 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3265 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003266
3267 synchronized (mLock) {
3268 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003269 proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3270 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3271 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3272 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3273 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3274 proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3275 mLastHeartbeatTime - nowUptime);
3276 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3277 mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
Makoto Onuki0525b982018-05-02 14:46:59 -07003278 proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003279
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003280 for (int u : mStartedUsers) {
3281 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3282 }
3283 if (mJobs.size() > 0) {
3284 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3285 sortJobs(jobs);
3286 for (JobStatus job : jobs) {
3287 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3288 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3289
3290 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003291 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003292 continue;
3293 }
3294
3295 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3296
3297 // isReadyToBeExecuted
3298 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3299 job.isReady());
3300 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3301 ArrayUtils.contains(mStartedUsers, job.getUserId()));
3302 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3303 mPendingJobs.contains(job));
3304 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3305 isCurrentlyActiveLocked(job));
3306 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3307 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3308 boolean componentPresent = false;
3309 try {
3310 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3311 job.getServiceComponent(),
3312 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3313 job.getUserId()) != null);
3314 } catch (RemoteException e) {
3315 }
3316 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3317 componentPresent);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003318 proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003319
3320 proto.end(rjToken);
3321 }
3322 }
3323 for (StateController controller : mControllers) {
3324 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003325 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003326 }
3327 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3328 int uid = mUidPriorityOverride.keyAt(i);
3329 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3330 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3331 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3332 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3333 mUidPriorityOverride.valueAt(i));
3334 proto.end(pToken);
3335 }
3336 }
3337 for (int i = 0; i < mBackingUpUids.size(); i++) {
3338 int uid = mBackingUpUids.keyAt(i);
3339 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3340 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3341 }
3342 }
3343
3344 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3345 filterUidFinal);
3346 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3347 filterUidFinal);
3348
3349 for (JobStatus job : mPendingJobs) {
3350 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3351
3352 job.writeToShortProto(proto, PendingJob.INFO);
3353 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3354 int priority = evaluateJobPriorityLocked(job);
3355 if (priority != JobInfo.PRIORITY_DEFAULT) {
3356 proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3357 }
3358 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3359
3360 proto.end(pjToken);
3361 }
3362 for (JobServiceContext jsc : mActiveServices) {
3363 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3364 final JobStatus job = jsc.getRunningJobLocked();
3365
3366 if (job == null) {
3367 final long ijToken = proto.start(ActiveJob.INACTIVE);
3368
3369 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3370 nowElapsed - jsc.mStoppedTime);
3371 if (jsc.mStoppedReason != null) {
3372 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3373 jsc.mStoppedReason);
3374 }
3375
3376 proto.end(ijToken);
3377 } else {
3378 final long rjToken = proto.start(ActiveJob.RUNNING);
3379
3380 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3381
3382 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3383 nowElapsed - jsc.getExecutionStartTimeElapsed());
3384 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3385 jsc.getTimeoutElapsed() - nowElapsed);
3386
3387 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3388
3389 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3390 if (priority != JobInfo.PRIORITY_DEFAULT) {
3391 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3392 }
3393
3394 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3395 nowUptime - job.madeActive);
3396 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3397 job.madeActive - job.madePending);
3398
3399 proto.end(rjToken);
3400 }
3401 proto.end(ajToken);
3402 }
3403 if (filterUid == -1) {
3404 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3405 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3406 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3407 }
3408 }
3409
3410 proto.flush();
3411 }
Christopher Tate7060b042014-06-09 19:50:00 -07003412}