blob: 07a99084e9d5dd8b85f2f27e9f88291baec63546 [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;
Kweku Adamsc9793092019-04-30 11:54:48 -070021import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
Christopher Tateb5c07882016-05-26 17:11:09 -070022
Kweku Adams649e2fc2019-03-18 14:55:38 -070023import android.annotation.NonNull;
Christopher Tate435c2f42018-01-18 12:59:15 -080024import android.annotation.UserIdInt;
Christopher Tateee7805b2016-07-15 16:56:56 -070025import android.app.Activity;
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070026import android.app.ActivityManager;
Makoto Onuki15407842018-01-19 14:23:11 -080027import android.app.ActivityManagerInternal;
Christopher Tate5568f542014-06-18 13:53:31 -070028import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070029import android.app.IUidObserver;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070030import android.app.job.IJobScheduler;
Christopher Tate7060b042014-06-09 19:50:00 -070031import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000032import android.app.job.JobParameters;
Tej Singh33a412b2018-03-16 18:43:59 -070033import android.app.job.JobProtoEnums;
Christopher Tate7060b042014-06-09 19:50:00 -070034import android.app.job.JobScheduler;
35import android.app.job.JobService;
Serik Beketayev75915d12018-08-01 16:56:59 -070036import android.app.job.JobSnapshot;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070037import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080038import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070039import android.app.usage.UsageStatsManagerInternal;
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;
Kweku Adams9239a1a2019-11-12 12:06:37 -080046import android.content.pm.ApplicationInfo;
Christopher Tate5568f542014-06-18 13:53:31 -070047import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070048import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060049import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070050import android.content.pm.PackageManagerInternal;
Kweku Adamsd1f4b902019-05-07 12:22:47 -070051import android.content.pm.ParceledListSlice;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070052import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070053import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070054import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070055import android.os.BatteryStats;
Amith Yamasani977e11f2018-02-16 11:29:54 -080056import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070057import android.os.Binder;
58import android.os.Handler;
59import android.os.Looper;
60import android.os.Message;
Makoto Onukib8472182019-11-13 12:03:22 -080061import android.os.ParcelFileDescriptor;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070062import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070063import android.os.RemoteException;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070064import android.os.ServiceManager;
Christopher Tate7060b042014-06-09 19:50:00 -070065import android.os.SystemClock;
66import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070067import android.os.UserManagerInternal;
Kweku Adamsbffea5a2018-12-13 22:13:28 -080068import android.os.WorkSource;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070069import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080070import android.text.format.DateUtils;
Kweku Adams9239a1a2019-11-12 12:06:37 -080071import android.util.ArrayMap;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070072import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070073import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070074import android.util.Slog;
75import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080076import android.util.SparseIntArray;
77import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080078import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080079
Suprabh Shukla08126472020-02-04 20:46:35 -080080import com.android.internal.R;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070081import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070082import com.android.internal.app.IBatteryStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070083import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060084import com.android.internal.util.DumpUtils;
Muhammad Qureshie9aec322020-01-28 10:27:18 -080085import com.android.internal.util.FrameworkStatsLog;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070086import com.android.internal.util.IndentingPrintWriter;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070087import com.android.server.AppStateTracker;
Makoto Onukieaf7eed2019-08-12 15:27:15 -070088import com.android.server.DeviceIdleInternal;
Christopher Tate616541d2017-07-26 14:27:38 -070089import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080090import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080091import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
92import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
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;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800100import com.android.server.job.controllers.QuotaController;
Kweku Adamsae9d6be2020-01-14 16:10:14 -0800101import com.android.server.job.controllers.RestrictingController;
Christopher Tate7060b042014-06-09 19:50:00 -0700102import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700103import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700104import com.android.server.job.controllers.TimeController;
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +0200105import com.android.server.job.restrictions.JobRestriction;
106import com.android.server.job.restrictions.ThermalStatusRestriction;
Christopher Tateb909c4d52019-10-21 12:50:37 -0700107import com.android.server.usage.AppStandbyInternal;
108import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener;
Kweku Adams9239a1a2019-11-12 12:06:37 -0800109import com.android.server.utils.quota.Categorizer;
110import com.android.server.utils.quota.Category;
111import com.android.server.utils.quota.CountQuotaTracker;
Christopher Tate7060b042014-06-09 19:50:00 -0700112
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700113import libcore.util.EmptyArray;
114
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700115import java.io.FileDescriptor;
116import java.io.PrintWriter;
117import java.time.Clock;
Makoto Onuki0e1ce972019-10-09 12:51:55 -0700118import java.time.Instant;
119import java.time.ZoneId;
120import java.time.ZoneOffset;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700121import java.util.ArrayList;
122import java.util.Arrays;
123import java.util.Collections;
124import java.util.Comparator;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700125import java.util.List;
Daulet Zhanguzin489e9c32019-12-30 12:45:49 +0000126import java.util.Objects;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700127import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800128import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700129
Christopher Tate7060b042014-06-09 19:50:00 -0700130/**
131 * Responsible for taking jobs representing work to be performed by a client app, and determining
132 * based on the criteria specified when that job should be run against the client application's
133 * endpoint.
134 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
135 * about constraints, or the state of active jobs. It receives callbacks from the various
136 * controllers and completed jobs and operates accordingly.
137 *
138 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
139 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
140 * @hide
141 */
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600142public class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700143 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700144 public static final String TAG = "JobScheduler";
145 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700146 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800147
Dianne Hackborn970510b2016-02-24 16:56:42 -0800148 /** The maximum number of concurrent jobs we run at one time. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800149 static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800150 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800151 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800152 /** The maximum number of jobs that we allow an unprivileged app to schedule */
153 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700154
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700155 @VisibleForTesting
156 public static Clock sSystemClock = Clock.systemUTC();
Makoto Onuki0e1ce972019-10-09 12:51:55 -0700157
158 private abstract static class MySimpleClock extends Clock {
159 private final ZoneId mZoneId;
160
161 MySimpleClock(ZoneId zoneId) {
162 this.mZoneId = zoneId;
163 }
164
165 @Override
166 public ZoneId getZone() {
167 return mZoneId;
168 }
169
170 @Override
171 public Clock withZone(ZoneId zone) {
172 return new MySimpleClock(zone) {
173 @Override
174 public long millis() {
175 return MySimpleClock.this.millis();
176 }
177 };
178 }
179
180 @Override
181 public abstract long millis();
182
183 @Override
184 public Instant instant() {
185 return Instant.ofEpochMilli(millis());
186 }
187 }
188
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700189 @VisibleForTesting
Makoto Onuki0e1ce972019-10-09 12:51:55 -0700190 public static Clock sUptimeMillisClock = new MySimpleClock(ZoneOffset.UTC) {
191 @Override
192 public long millis() {
193 return SystemClock.uptimeMillis();
194 }
195 };
196
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700197 @VisibleForTesting
Makoto Onuki0e1ce972019-10-09 12:51:55 -0700198 public static Clock sElapsedRealtimeClock = new MySimpleClock(ZoneOffset.UTC) {
199 @Override
200 public long millis() {
201 return SystemClock.elapsedRealtime();
202 }
203 };
Christopher Tate2f36fd62016-02-18 18:36:08 -0800204
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800205 /** Global local for all job scheduler state. */
206 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700207 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700208 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700209 /** Tracking the standby bucket state of each app */
210 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700211 /** Tracking amount of time each package runs for. */
212 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Makoto Onuki714f97d2018-12-05 11:18:13 -0800213 final JobConcurrencyManager mConcurrencyManager;
Christopher Tate7060b042014-06-09 19:50:00 -0700214
215 static final int MSG_JOB_EXPIRED = 0;
216 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700217 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000218 static final int MSG_CHECK_JOB_GREEDY = 3;
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800219 static final int MSG_UID_STATE_CHANGED = 4;
220 static final int MSG_UID_GONE = 5;
221 static final int MSG_UID_ACTIVE = 6;
222 static final int MSG_UID_IDLE = 7;
Christopher Tate7060b042014-06-09 19:50:00 -0700223
Christopher Tate7060b042014-06-09 19:50:00 -0700224 /**
225 * Track Services that have currently active or pending jobs. The index is provided by
226 * {@link JobStatus#getServiceToken()}
227 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700228 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700229
Christopher Tate7060b042014-06-09 19:50:00 -0700230 /** List of controllers that will notify this service of updates to jobs. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800231 final List<StateController> mControllers;
Kweku Adamsae9d6be2020-01-14 16:10:14 -0800232 /**
233 * List of controllers that will apply to all jobs in the RESTRICTED bucket. This is a subset of
234 * {@link #mControllers}.
235 */
236 private final List<RestrictingController> mRestrictiveControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800237 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700238 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700239 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700240 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700241 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700242 private final DeviceIdleJobsController mDeviceIdleJobsController;
Kweku Adamsb4b8b9c2019-06-13 18:37:51 -0700243 /** Needed to get remaining quota time. */
244 private final QuotaController mQuotaController;
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +0200245 /**
246 * List of restrictions.
247 * Note: do not add to or remove from this list at runtime except in the constructor, because we
248 * do not synchronize access to this list.
249 */
250 private final List<JobRestriction> mJobRestrictions;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700251
Suprabh Shukla08126472020-02-04 20:46:35 -0800252 @NonNull
253 private final String mSystemGalleryPackage;
254
Kweku Adams9239a1a2019-11-12 12:06:37 -0800255 private final CountQuotaTracker mQuotaTracker;
Kweku Adamsfd983782020-01-14 15:26:23 -0800256 private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
Kweku Adams9239a1a2019-11-12 12:06:37 -0800257
Christopher Tate7060b042014-06-09 19:50:00 -0700258 /**
259 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
260 * when ready to execute them.
261 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700262 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700263
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700264 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700265
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700266 final JobHandler mHandler;
267 final JobSchedulerStub mJobSchedulerStub;
268
Christopher Tatea732f012017-10-26 17:26:53 -0700269 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800270 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700271 IBatteryStats mBatteryStats;
Makoto Onukieaf7eed2019-08-12 15:27:15 -0700272 DeviceIdleInternal mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800273 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800274 final UsageStatsManagerInternal mUsageStats;
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800275 private final AppStandbyInternal mAppStandbyInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700276
277 /**
278 * Set to true once we are allowed to run third party apps.
279 */
280 boolean mReadyToRock;
281
Christopher Tate7060b042014-06-09 19:50:00 -0700282 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800283 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800284 */
285 boolean mReportedActive;
286
287 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800288 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800289 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800290 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
291
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700292 /**
293 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
294 */
295 final SparseIntArray mBackingUpUids = new SparseIntArray();
296
Christopher Tatea732f012017-10-26 17:26:53 -0700297 /**
Kweku Adams9239a1a2019-11-12 12:06:37 -0800298 * Cache of debuggable app status.
299 */
300 final ArrayMap<String, Boolean> mDebuggableApps = new ArrayMap<>();
301
302 /**
Kweku Adams6c5dc642019-07-25 10:29:38 -0700303 * Named indices into standby bucket arrays, for clarity in referring to
Christopher Tate325768c2018-03-07 16:07:56 -0800304 * specific buckets' bookkeeping.
305 */
Kweku Adams4836f9d2018-11-12 17:04:17 -0800306 public static final int ACTIVE_INDEX = 0;
307 public static final int WORKING_INDEX = 1;
308 public static final int FREQUENT_INDEX = 2;
309 public static final int RARE_INDEX = 3;
310 public static final int NEVER_INDEX = 4;
Kweku Adamsae9d6be2020-01-14 16:10:14 -0800311 // Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
312 // (ScheduledJobStateChanged and JobStatusDumpProto).
313 public static final int RESTRICTED_INDEX = 5;
Christopher Tate325768c2018-03-07 16:07:56 -0800314
Dianne Hackborn970510b2016-02-24 16:56:42 -0800315 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
316
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700317 private class ConstantsObserver extends ContentObserver {
318 private ContentResolver mResolver;
319
320 public ConstantsObserver(Handler handler) {
321 super(handler);
322 }
323
324 public void start(ContentResolver resolver) {
325 mResolver = resolver;
326 mResolver.registerContentObserver(Settings.Global.getUriFor(
327 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
328 updateConstants();
329 }
330
331 @Override
332 public void onChange(boolean selfChange, Uri uri) {
333 updateConstants();
334 }
335
336 private void updateConstants() {
337 synchronized (mLock) {
338 try {
339 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
340 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Kweku Adams4836f9d2018-11-12 17:04:17 -0800341 for (int controller = 0; controller < mControllers.size(); controller++) {
342 final StateController sc = mControllers.get(controller);
343 sc.onConstantsUpdatedLocked();
344 }
Kweku Adams9239a1a2019-11-12 12:06:37 -0800345 mQuotaTracker.setEnabled(mConstants.ENABLE_API_QUOTAS);
346 mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
347 mConstants.API_QUOTA_SCHEDULE_COUNT,
348 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700349 } catch (IllegalArgumentException e) {
350 // Failed to parse the settings string, log this and move on
351 // with defaults.
352 Slog.e(TAG, "Bad jobscheduler settings", e);
353 }
354 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700355 }
356 }
357
Makoto Onukic279f2b2019-01-09 13:11:37 -0800358 static class MaxJobCounts {
Makoto Onuki66a51442018-12-20 14:23:50 -0800359 private final KeyValueListParser.IntValue mTotal;
Makoto Onukic279f2b2019-01-09 13:11:37 -0800360 private final KeyValueListParser.IntValue mMaxBg;
361 private final KeyValueListParser.IntValue mMinBg;
Makoto Onuki66a51442018-12-20 14:23:50 -0800362
Makoto Onukic279f2b2019-01-09 13:11:37 -0800363 MaxJobCounts(int totalDefault, String totalKey,
364 int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
Makoto Onuki66a51442018-12-20 14:23:50 -0800365 mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800366 mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
367 mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
Makoto Onuki66a51442018-12-20 14:23:50 -0800368 }
369
370 public void parse(KeyValueListParser parser) {
371 mTotal.parse(parser);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800372 mMaxBg.parse(parser);
373 mMinBg.parse(parser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800374
Makoto Onukic279f2b2019-01-09 13:11:37 -0800375 if (mTotal.getValue() < 1) {
376 mTotal.setValue(1);
377 } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
378 mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
Makoto Onuki66a51442018-12-20 14:23:50 -0800379 }
380
Makoto Onukic279f2b2019-01-09 13:11:37 -0800381 if (mMaxBg.getValue() < 1) {
382 mMaxBg.setValue(1);
383 } else if (mMaxBg.getValue() > mTotal.getValue()) {
384 mMaxBg.setValue(mTotal.getValue());
385 }
386 if (mMinBg.getValue() < 0) {
387 mMinBg.setValue(0);
388 } else {
389 if (mMinBg.getValue() > mMaxBg.getValue()) {
390 mMinBg.setValue(mMaxBg.getValue());
391 }
392 if (mMinBg.getValue() >= mTotal.getValue()) {
393 mMinBg.setValue(mTotal.getValue() - 1);
394 }
395 }
Makoto Onuki66a51442018-12-20 14:23:50 -0800396 }
397
Makoto Onukif731c422019-01-22 13:23:31 -0800398 /** Total number of jobs to run simultaneously. */
399 public int getMaxTotal() {
Makoto Onuki66a51442018-12-20 14:23:50 -0800400 return mTotal.getValue();
401 }
402
Makoto Onukif731c422019-01-22 13:23:31 -0800403 /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
Makoto Onukic279f2b2019-01-09 13:11:37 -0800404 public int getMaxBg() {
405 return mMaxBg.getValue();
406 }
407
Makoto Onukif731c422019-01-22 13:23:31 -0800408 /**
409 * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
410 * pending, rather than always running the TOTAL number of FG jobs.
411 */
Makoto Onukic279f2b2019-01-09 13:11:37 -0800412 public int getMinBg() {
413 return mMinBg.getValue();
Makoto Onuki66a51442018-12-20 14:23:50 -0800414 }
415
416 public void dump(PrintWriter pw, String prefix) {
417 mTotal.dump(pw, prefix);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800418 mMaxBg.dump(pw, prefix);
419 mMinBg.dump(pw, prefix);
Makoto Onuki66a51442018-12-20 14:23:50 -0800420 }
421
Makoto Onukif731c422019-01-22 13:23:31 -0800422 public void dumpProto(ProtoOutputStream proto, long fieldId) {
423 final long token = proto.start(fieldId);
424 mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
425 mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
426 mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
427 proto.end(token);
428 }
429 }
430
431 /** {@link MaxJobCounts} for each memory trim level. */
432 static class MaxJobCountsPerMemoryTrimLevel {
433 public final MaxJobCounts normal;
434 public final MaxJobCounts moderate;
435 public final MaxJobCounts low;
436 public final MaxJobCounts critical;
437
438 MaxJobCountsPerMemoryTrimLevel(
439 MaxJobCounts normal,
440 MaxJobCounts moderate, MaxJobCounts low,
441 MaxJobCounts critical) {
442 this.normal = normal;
443 this.moderate = moderate;
444 this.low = low;
445 this.critical = critical;
446 }
447
448 public void dumpProto(ProtoOutputStream proto, long fieldId) {
449 final long token = proto.start(fieldId);
450 normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL);
451 moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE);
452 low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW);
453 critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL);
454 proto.end(token);
Makoto Onuki66a51442018-12-20 14:23:50 -0800455 }
456 }
457
Wei Wang8c0c3c12018-11-14 14:56:52 -0800458 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700459 * All times are in milliseconds. These constants are kept synchronized with the system
460 * global Settings. Any access to this class or its fields should be done while
461 * holding the JobSchedulerService.mLock lock.
462 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700463 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700464 // Key names stored in the settings value.
Kweku Adams111b8222020-02-11 12:06:26 -0800465 // TODO(124466289): remove deprecated flags when we migrate to DeviceConfig
466 private static final String DEPRECATED_KEY_MIN_IDLE_COUNT = "min_idle_count";
467 private static final String DEPRECATED_KEY_MIN_CHARGING_COUNT = "min_charging_count";
468 private static final String DEPRECATED_KEY_MIN_BATTERY_NOT_LOW_COUNT =
469 "min_battery_not_low_count";
470 private static final String DEPRECATED_KEY_MIN_STORAGE_NOT_LOW_COUNT =
471 "min_storage_not_low_count";
472 private static final String DEPRECATED_KEY_MIN_CONNECTIVITY_COUNT =
473 "min_connectivity_count";
474 private static final String DEPRECATED_KEY_MIN_CONTENT_COUNT = "min_content_count";
475 private static final String DEPRECATED_KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
Kweku Adams41cb3322019-06-26 14:42:26 -0700476 private static final String KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT =
477 "min_ready_non_active_jobs_count";
478 private static final String KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS =
479 "max_non_active_job_batch_delay_ms";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700480 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
481 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
Makoto Onukic279f2b2019-01-09 13:11:37 -0800482
483 // The following values used to be used on P and below. Do not reuse them.
484 private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
485 private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
486 private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
487 private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
488 private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
489
Kweku Adams7a1c76d2020-02-06 16:54:15 -0800490 private static final String DEPRECATED_KEY_MAX_STANDARD_RESCHEDULE_COUNT
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700491 = "max_standard_reschedule_count";
Kweku Adams7a1c76d2020-02-06 16:54:15 -0800492 private static final String DEPRECATED_KEY_MAX_WORK_RESCHEDULE_COUNT =
493 "max_work_reschedule_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700494 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
495 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Kweku Adams6c5dc642019-07-25 10:29:38 -0700496 private static final String DEPRECATED_KEY_STANDBY_HEARTBEAT_TIME =
497 "standby_heartbeat_time";
498 private static final String DEPRECATED_KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
499 private static final String DEPRECATED_KEY_STANDBY_FREQUENT_BEATS =
500 "standby_frequent_beats";
501 private static final String DEPRECATED_KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700502 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
503 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Kweku Adams6c5dc642019-07-25 10:29:38 -0700504 private static final String DEPRECATED_KEY_USE_HEARTBEATS = "use_heartbeats";
Kweku Adams9239a1a2019-11-12 12:06:37 -0800505 private static final String KEY_ENABLE_API_QUOTAS = "enable_api_quotas";
506 private static final String KEY_API_QUOTA_SCHEDULE_COUNT = "aq_schedule_count";
507 private static final String KEY_API_QUOTA_SCHEDULE_WINDOW_MS = "aq_schedule_window_ms";
508 private static final String KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION =
509 "aq_schedule_throw_exception";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700510
Kweku Adams41cb3322019-06-26 14:42:26 -0700511 private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
512 private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700513 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
514 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700515 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
516 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700517 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
518 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Kweku Adams9239a1a2019-11-12 12:06:37 -0800519 private static final boolean DEFAULT_ENABLE_API_QUOTAS = true;
Kweku Adamsfd983782020-01-14 15:26:23 -0800520 private static final int DEFAULT_API_QUOTA_SCHEDULE_COUNT = 250;
Kweku Adams9239a1a2019-11-12 12:06:37 -0800521 private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
522 private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700523
524 /**
Kweku Adams41cb3322019-06-26 14:42:26 -0700525 * Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
526 */
527 int MIN_READY_NON_ACTIVE_JOBS_COUNT = DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT;
528
529 /**
530 * Don't batch a non-ACTIVE job if it's been delayed due to force batching attempts for
531 * at least this amount of time.
532 */
533 long MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
534
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700535 /**
536 * This is the job execution factor that is considered to be heavy use of the system.
537 */
538 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
539 /**
540 * This is the job execution factor that is considered to be moderate use of the system.
541 */
542 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
Makoto Onuki66a51442018-12-20 14:23:50 -0800543
544 // Max job counts for screen on / off, for each memory trim level.
Makoto Onukif731c422019-01-22 13:23:31 -0800545 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
546 new MaxJobCountsPerMemoryTrimLevel(
547 new MaxJobCounts(
548 8, "max_job_total_on_normal",
549 6, "max_job_max_bg_on_normal",
550 2, "max_job_min_bg_on_normal"),
551 new MaxJobCounts(
552 8, "max_job_total_on_moderate",
553 4, "max_job_max_bg_on_moderate",
554 2, "max_job_min_bg_on_moderate"),
555 new MaxJobCounts(
556 5, "max_job_total_on_low",
557 1, "max_job_max_bg_on_low",
558 1, "max_job_min_bg_on_low"),
559 new MaxJobCounts(
560 5, "max_job_total_on_critical",
561 1, "max_job_max_bg_on_critical",
562 1, "max_job_min_bg_on_critical"));
Makoto Onuki66a51442018-12-20 14:23:50 -0800563
Makoto Onukif731c422019-01-22 13:23:31 -0800564 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
565 new MaxJobCountsPerMemoryTrimLevel(
566 new MaxJobCounts(
567 10, "max_job_total_off_normal",
568 6, "max_job_max_bg_off_normal",
569 2, "max_job_min_bg_off_normal"),
570 new MaxJobCounts(
571 10, "max_job_total_off_moderate",
572 4, "max_job_max_bg_off_moderate",
573 2, "max_job_min_bg_off_moderate"),
574 new MaxJobCounts(
575 5, "max_job_total_off_low",
576 1, "max_job_max_bg_off_low",
577 1, "max_job_min_bg_off_low"),
578 new MaxJobCounts(
579 5, "max_job_total_off_critical",
580 1, "max_job_max_bg_off_critical",
581 1, "max_job_min_bg_off_critical"));
Makoto Onuki66a51442018-12-20 14:23:50 -0800582
Makoto Onukic279f2b2019-01-09 13:11:37 -0800583
584 /** Wait for this long after screen off before increasing the job concurrency. */
585 final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
586 new KeyValueListParser.IntValue(
587 "screen_off_job_concurrency_increase_delay_ms", 30_000);
Makoto Onuki66a51442018-12-20 14:23:50 -0800588
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700589 /**
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700590 * The minimum backoff time to allow for linear backoff.
591 */
592 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
593 /**
594 * The minimum backoff time to allow for exponential backoff.
595 */
596 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Kweku Adams6c5dc642019-07-25 10:29:38 -0700597
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700598 /**
599 * The fraction of a job's running window that must pass before we
600 * consider running it when the network is congested.
601 */
602 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
603 /**
604 * The fraction of a prefetch job's running window that must pass before
605 * we consider matching it against a metered network.
606 */
607 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800608
Kweku Adams9239a1a2019-11-12 12:06:37 -0800609 /**
610 * Whether to enable quota limits on APIs.
611 */
612 public boolean ENABLE_API_QUOTAS = DEFAULT_ENABLE_API_QUOTAS;
613 /**
614 * The maximum number of schedule() calls an app can make in a set amount of time.
615 */
616 public int API_QUOTA_SCHEDULE_COUNT = DEFAULT_API_QUOTA_SCHEDULE_COUNT;
617 /**
618 * The time window that {@link #API_QUOTA_SCHEDULE_COUNT} should be evaluated over.
619 */
620 public long API_QUOTA_SCHEDULE_WINDOW_MS = DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS;
621 /**
622 * Whether to throw an exception when an app hits its schedule quota limit.
623 */
624 public boolean API_QUOTA_SCHEDULE_THROW_EXCEPTION =
625 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION;
626
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700627 private final KeyValueListParser mParser = new KeyValueListParser(',');
628
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700629 void updateConstantsLocked(String value) {
630 try {
631 mParser.setString(value);
632 } catch (Exception e) {
633 // Failed to parse the settings string, log this and move on
634 // with defaults.
635 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700636 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800637
Kweku Adams41cb3322019-06-26 14:42:26 -0700638 MIN_READY_NON_ACTIVE_JOBS_COUNT = mParser.getInt(
639 KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
640 DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT);
641 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = mParser.getLong(
642 KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
643 DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700644 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
645 DEFAULT_HEAVY_USE_FACTOR);
646 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
647 DEFAULT_MODERATE_USE_FACTOR);
Makoto Onuki66a51442018-12-20 14:23:50 -0800648
Makoto Onukif731c422019-01-22 13:23:31 -0800649 MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
650 MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
651 MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
652 MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800653
Makoto Onukif731c422019-01-22 13:23:31 -0800654 MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
655 MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
656 MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
657 MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800658
Makoto Onukif996c2c2019-01-25 15:18:48 -0800659 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.parse(mParser);
660
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700661 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
662 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
663 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
664 DEFAULT_MIN_EXP_BACKOFF_TIME);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700665 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
666 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
667 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
668 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Kweku Adams9239a1a2019-11-12 12:06:37 -0800669
670 ENABLE_API_QUOTAS = mParser.getBoolean(KEY_ENABLE_API_QUOTAS,
671 DEFAULT_ENABLE_API_QUOTAS);
Kweku Adamsfd983782020-01-14 15:26:23 -0800672 // Set a minimum value on the quota limit so it's not so low that it interferes with
673 // legitimate use cases.
Kweku Adams9239a1a2019-11-12 12:06:37 -0800674 API_QUOTA_SCHEDULE_COUNT = Math.max(250,
675 mParser.getInt(KEY_API_QUOTA_SCHEDULE_COUNT, DEFAULT_API_QUOTA_SCHEDULE_COUNT));
676 API_QUOTA_SCHEDULE_WINDOW_MS = mParser.getDurationMillis(
677 KEY_API_QUOTA_SCHEDULE_WINDOW_MS, DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS);
678 API_QUOTA_SCHEDULE_THROW_EXCEPTION = mParser.getBoolean(
679 KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
680 DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700681 }
682
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700683 void dump(IndentingPrintWriter pw) {
684 pw.println("Settings:");
685 pw.increaseIndent();
Kweku Adams41cb3322019-06-26 14:42:26 -0700686 pw.printPair(KEY_MIN_READY_NON_ACTIVE_JOBS_COUNT,
687 MIN_READY_NON_ACTIVE_JOBS_COUNT).println();
688 pw.printPair(KEY_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
689 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS).println();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700690 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
691 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
Makoto Onuki66a51442018-12-20 14:23:50 -0800692
Makoto Onukif731c422019-01-22 13:23:31 -0800693 MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, "");
694 MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, "");
695 MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, "");
696 MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, "");
Makoto Onuki66a51442018-12-20 14:23:50 -0800697
Makoto Onukif731c422019-01-22 13:23:31 -0800698 MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, "");
699 MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, "");
700 MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
701 MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
702
703 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
Makoto Onuki66a51442018-12-20 14:23:50 -0800704
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700705 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
706 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700707 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
708 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
Kweku Adams4e774a32019-04-24 18:01:29 -0700709
Kweku Adams9239a1a2019-11-12 12:06:37 -0800710 pw.printPair(KEY_ENABLE_API_QUOTAS, ENABLE_API_QUOTAS).println();
711 pw.printPair(KEY_API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT).println();
712 pw.printPair(KEY_API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS).println();
713 pw.printPair(KEY_API_QUOTA_SCHEDULE_THROW_EXCEPTION,
714 API_QUOTA_SCHEDULE_THROW_EXCEPTION).println();
715
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700716 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700717 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800718
Kweku Adams4e774a32019-04-24 18:01:29 -0700719 void dump(ProtoOutputStream proto) {
Kweku Adams41cb3322019-06-26 14:42:26 -0700720 proto.write(ConstantsProto.MIN_READY_NON_ACTIVE_JOBS_COUNT,
721 MIN_READY_NON_ACTIVE_JOBS_COUNT);
722 proto.write(ConstantsProto.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS,
723 MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800724 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
725 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
Makoto Onuki66a51442018-12-20 14:23:50 -0800726
Makoto Onukif731c422019-01-22 13:23:31 -0800727 MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
728 MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
729
730 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
731 ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
Makoto Onuki66a51442018-12-20 14:23:50 -0800732
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800733 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
734 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700735 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
736 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams9239a1a2019-11-12 12:06:37 -0800737
738 proto.write(ConstantsProto.ENABLE_API_QUOTAS, ENABLE_API_QUOTAS);
739 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_COUNT, API_QUOTA_SCHEDULE_COUNT);
740 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_WINDOW_MS, API_QUOTA_SCHEDULE_WINDOW_MS);
741 proto.write(ConstantsProto.API_QUOTA_SCHEDULE_THROW_EXCEPTION,
742 API_QUOTA_SCHEDULE_THROW_EXCEPTION);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800743 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700744 }
745
746 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700747 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700748
Kweku Adamscdc2c992020-03-13 13:24:53 -0700749 private static final Comparator<JobStatus> sPendingJobComparator = (o1, o2) -> {
750 // Jobs with an override state set (via adb) should be put first as tests/developers
751 // expect the jobs to run immediately.
752 if (o1.overrideState != o2.overrideState) {
753 // Higher override state (OVERRIDE_FULL) should be before lower state (OVERRIDE_SOFT)
754 return o2.overrideState - o1.overrideState;
755 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700756 if (o1.enqueueTime < o2.enqueueTime) {
757 return -1;
758 }
759 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
760 };
761
762 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
763 int where = Collections.binarySearch(array, newItem, comparator);
764 if (where < 0) {
765 where = ~where;
766 }
767 array.add(where, newItem);
768 }
769
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700770 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700771 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
772 * still clean up. On reinstall the package will have a new uid.
773 */
774 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
775 @Override
776 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700777 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700778 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700779 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700780 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700781 final String pkgName = getPackageName(intent);
782 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
783
Christopher Tateee7805b2016-07-15 16:56:56 -0700784 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700785 // Purge the app's jobs if the whole package was just disabled. When this is
786 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700787 if (pkgName != null && pkgUid != -1) {
788 final String[] changedComponents = intent.getStringArrayExtra(
789 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
790 if (changedComponents != null) {
791 for (String component : changedComponents) {
792 if (component.equals(pkgName)) {
793 if (DEBUG) {
794 Slog.d(TAG, "Package state change: " + pkgName);
795 }
796 try {
797 final int userId = UserHandle.getUserId(pkgUid);
798 IPackageManager pm = AppGlobals.getPackageManager();
799 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
800 if (state == COMPONENT_ENABLED_STATE_DISABLED
801 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
802 if (DEBUG) {
803 Slog.d(TAG, "Removing jobs for package " + pkgName
804 + " in user " + userId);
805 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700806 cancelJobsForPackageAndUid(pkgName, pkgUid,
807 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700808 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700809 } catch (RemoteException|IllegalArgumentException e) {
810 /*
811 * IllegalArgumentException means that the package doesn't exist.
812 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
813 * behind outright uninstall, so by the time we try to act it's gone.
814 * We don't need to act on this PACKAGE_CHANGED when this happens;
815 * we'll get a PACKAGE_REMOVED later and clean up then.
816 *
817 * RemoteException can't actually happen; the package manager is
818 * running in this same process.
819 */
820 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700821 break;
822 }
823 }
Kweku Adamscdbfcb92018-12-06 17:05:15 -0800824 if (DEBUG) {
825 Slog.d(TAG, "Something in " + pkgName
826 + " changed. Reevaluating controller states.");
827 }
828 synchronized (mLock) {
829 for (int c = mControllers.size() - 1; c >= 0; --c) {
830 mControllers.get(c).reevaluateStateLocked(pkgUid);
831 }
832 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700833 }
834 } else {
835 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
836 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700837 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700838 // If this is an outright uninstall rather than the first half of an
839 // app update sequence, cancel the jobs associated with the app.
840 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
841 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
842 if (DEBUG) {
843 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
844 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700845 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -0800846 synchronized (mLock) {
847 for (int c = 0; c < mControllers.size(); ++c) {
848 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
849 }
Kweku Adams9239a1a2019-11-12 12:06:37 -0800850 mDebuggableApps.remove(pkgName);
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -0800851 }
Christopher Tate7060b042014-06-09 19:50:00 -0700852 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700853 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700854 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
855 if (DEBUG) {
856 Slog.d(TAG, "Removing jobs for user: " + userId);
857 }
858 cancelJobsForUser(userId);
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -0800859 synchronized (mLock) {
860 for (int c = 0; c < mControllers.size(); ++c) {
861 mControllers.get(c).onUserRemovedLocked(userId);
862 }
863 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700864 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
865 // Has this package scheduled any jobs, such that we will take action
866 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700867 if (pkgUid != -1) {
868 List<JobStatus> jobsForUid;
869 synchronized (mLock) {
870 jobsForUid = mJobs.getJobsByUid(pkgUid);
871 }
872 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
873 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
874 if (DEBUG) {
875 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
876 + pkgUid + " has jobs");
877 }
878 setResultCode(Activity.RESULT_OK);
879 break;
880 }
881 }
882 }
883 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
884 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700885 if (pkgUid != -1) {
886 if (DEBUG) {
887 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
888 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700889 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700890 }
Christopher Tate7060b042014-06-09 19:50:00 -0700891 }
892 }
893 };
894
Christopher Tateb5c07882016-05-26 17:11:09 -0700895 private String getPackageName(Intent intent) {
896 Uri uri = intent.getData();
897 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
898 return pkg;
899 }
900
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700901 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Hui Yu26969322019-08-21 14:56:35 -0700902 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
903 int capability) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800904 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700905 }
906
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700907 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800908 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700909 }
910
911 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800912 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700913 }
914
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700915 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800916 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700917 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700918
919 @Override public void onUidCachedChanged(int uid, boolean cached) {
920 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700921 };
922
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600923 public Context getTestableContext() {
924 return getContext();
925 }
926
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800927 public Object getLock() {
928 return mLock;
929 }
930
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700931 public JobStore getJobStore() {
932 return mJobs;
933 }
934
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700935 public Constants getConstants() {
936 return mConstants;
937 }
938
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800939 public boolean isChainedAttributionEnabled() {
940 return WorkSource.isChainedBatteryAttributionEnabled(getContext());
941 }
942
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700943 @Override
944 public void onStartUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -0800945 synchronized (mLock) {
946 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
947 }
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700948 // Let's kick any outstanding jobs for this user.
949 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
950 }
951
952 @Override
953 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700954 // Let's kick any outstanding jobs for this user.
955 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
956 }
957
958 @Override
959 public void onStopUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -0800960 synchronized (mLock) {
961 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
962 }
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700963 }
964
Makoto Onuki15407842018-01-19 14:23:11 -0800965 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800966 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800967 */
Makoto Onukie4918212018-02-06 11:30:15 -0800968 private boolean isUidActive(int uid) {
969 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800970 }
971
Makoto Onukie4918212018-02-06 11:30:15 -0800972 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800973
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700974 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
975 int userId, String tag) {
Kweku Adamsfd983782020-01-14 15:26:23 -0800976 if (job.isPersisted()) {
977 // Only limit schedule calls for persisted jobs.
978 final String pkg =
979 packageName == null ? job.getService().getPackageName() : packageName;
980 if (!mQuotaTracker.isWithinQuota(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG)) {
981 Slog.e(TAG, userId + "-" + pkg + " has called schedule() too many times");
Kweku Adamsc6a9b342020-01-08 18:37:26 -0800982 mAppStandbyInternal.restrictApp(
Kweku Adamsaa461942020-03-16 11:59:05 -0700983 pkg, userId, UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
Kweku Adamsf327a3d2020-02-12 16:11:56 -0800984 if (mConstants.API_QUOTA_SCHEDULE_THROW_EXCEPTION) {
Kweku Adamsfd983782020-01-14 15:26:23 -0800985 final boolean isDebuggable;
986 synchronized (mLock) {
987 if (!mDebuggableApps.containsKey(packageName)) {
988 try {
989 final ApplicationInfo appInfo = AppGlobals.getPackageManager()
990 .getApplicationInfo(pkg, 0, userId);
991 if (appInfo != null) {
992 mDebuggableApps.put(packageName,
993 (appInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
994 } else {
995 return JobScheduler.RESULT_FAILURE;
996 }
997 } catch (RemoteException e) {
998 throw new RuntimeException(e);
Kweku Adams9239a1a2019-11-12 12:06:37 -0800999 }
Kweku Adams9239a1a2019-11-12 12:06:37 -08001000 }
Kweku Adamsfd983782020-01-14 15:26:23 -08001001 isDebuggable = mDebuggableApps.get(packageName);
Kweku Adams9239a1a2019-11-12 12:06:37 -08001002 }
Kweku Adamsfd983782020-01-14 15:26:23 -08001003 if (isDebuggable) {
1004 // Only throw the exception for debuggable apps.
1005 throw new IllegalStateException(
1006 "schedule()/enqueue() called more than "
1007 + mQuotaTracker.getLimit(Category.SINGLE_CATEGORY)
1008 + " times in the past "
1009 + mQuotaTracker.getWindowSizeMs(Category.SINGLE_CATEGORY)
1010 + "ms");
1011 }
Kweku Adams9239a1a2019-11-12 12:06:37 -08001012 }
Kweku Adamsfd983782020-01-14 15:26:23 -08001013 return JobScheduler.RESULT_FAILURE;
Kweku Adams9239a1a2019-11-12 12:06:37 -08001014 }
Kweku Adamsfd983782020-01-14 15:26:23 -08001015 mQuotaTracker.noteEvent(userId, pkg, QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG);
Kweku Adams9239a1a2019-11-12 12:06:37 -08001016 }
Kweku Adams9239a1a2019-11-12 12:06:37 -08001017
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001018 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001019 if (ActivityManager.getService().isAppStartModeDisabled(uId,
1020 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001021 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1022 + " -- package not allowed to start");
1023 return JobScheduler.RESULT_FAILURE;
1024 }
1025 } catch (RemoteException e) {
1026 }
Christopher Tatea732f012017-10-26 17:26:53 -07001027
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001028 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001029 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1030
1031 if (work != null && toCancel != null) {
1032 // Fast path: we are adding work to an existing job, and the JobInfo is not
1033 // changing. We can just directly enqueue this work in to the job.
1034 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -08001035
Christopher Tate1b1fca82019-08-14 16:59:33 -07001036 toCancel.enqueueWorkLocked(work);
Makoto Onuki15407842018-01-19 14:23:11 -08001037
1038 // If any of work item is enqueued when the source is in the foreground,
1039 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -08001040 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001041
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001042 return JobScheduler.RESULT_SUCCESS;
1043 }
1044 }
1045
1046 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -08001047
1048 // Give exemption if the source is in the foreground just now.
1049 // Note if it's a sync job, this method is called on the handler so it's not exactly
1050 // the state when requestSync() was called, but that should be fine because of the
1051 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -08001052 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001053
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001054 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -08001055 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -08001056 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001057 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1058 Slog.w(TAG, "Too many jobs for uid " + uId);
1059 throw new IllegalStateException("Apps may not schedule more than "
1060 + MAX_JOBS_PER_APP + " distinct jobs");
1061 }
1062 }
1063
Dianne Hackborna47223f2017-03-30 13:49:13 -07001064 // This may throw a SecurityException.
Christopher Tate1b1fca82019-08-14 16:59:33 -07001065 jobStatus.prepareLocked();
Dianne Hackborna47223f2017-03-30 13:49:13 -07001066
Christopher Tate16032042018-10-05 15:53:50 -07001067 if (toCancel != null) {
1068 // Implicitly replaces the existing job record with the new instance
1069 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
1070 } else {
1071 startTrackingJobLocked(jobStatus, null);
1072 }
Christopher Tateb0da63c2019-10-09 14:26:52 -07001073
1074 if (work != null) {
1075 // If work has been supplied, enqueue it into the new job.
1076 jobStatus.enqueueWorkLocked(work);
1077 }
1078
Muhammad Qureshie9aec322020-01-28 10:27:18 -08001079 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -08001080 uId, null, jobStatus.getBatteryName(),
Muhammad Qureshie9aec322020-01-28 10:27:18 -08001081 FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
Ben Murdoch741e3352019-05-09 11:27:28 +01001082 JobProtoEnums.STOP_REASON_CANCELLED, jobStatus.getStandbyBucket(),
Suprabh Shukla1b5778d2020-02-18 16:39:02 -08001083 jobStatus.getJobId(),
1084 jobStatus.hasChargingConstraint(),
1085 jobStatus.hasBatteryNotLowConstraint(),
1086 jobStatus.hasStorageNotLowConstraint(),
1087 jobStatus.hasTimingDelayConstraint(),
1088 jobStatus.hasDeadlineConstraint(),
1089 jobStatus.hasIdleConstraint(),
1090 jobStatus.hasConnectivityConstraint(),
1091 jobStatus.hasContentTriggerConstraint());
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001092
1093 // If the job is immediately ready to run, then we can just immediately
1094 // put it in the pending list and try to schedule it. This is especially
1095 // important for jobs with a 0 deadline constraint, since they will happen a fair
1096 // amount, we want to handle them as quickly as possible, and semantically we want to
1097 // make sure we have started holding the wake lock for the job before returning to
1098 // the caller.
1099 // If the job is not yet ready to run, there is nothing more to do -- we are
1100 // now just waiting for one of its controllers to change state and schedule
1101 // the job appropriately.
1102 if (isReadyToBeExecutedLocked(jobStatus)) {
1103 // This is a new job, we can just immediately put it on the pending
1104 // list and try to run it.
1105 mJobPackageTracker.notePending(jobStatus);
Kweku Adamscdc2c992020-03-13 13:24:53 -07001106 addOrderedItem(mPendingJobs, jobStatus, sPendingJobComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001107 maybeRunPendingJobsLocked();
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001108 } else {
1109 evaluateControllerStatesLocked(jobStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001110 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001111 }
Christopher Tate7060b042014-06-09 19:50:00 -07001112 return JobScheduler.RESULT_SUCCESS;
1113 }
1114
1115 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001116 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001117 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1118 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
1119 for (int i = jobs.size() - 1; i >= 0; i--) {
1120 JobStatus job = jobs.get(i);
1121 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -07001122 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001123 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -07001124 }
Christopher Tate7060b042014-06-09 19:50:00 -07001125 }
1126
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001127 public JobInfo getPendingJob(int uid, int jobId) {
1128 synchronized (mLock) {
1129 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1130 for (int i = jobs.size() - 1; i >= 0; i--) {
1131 JobStatus job = jobs.get(i);
1132 if (job.getJobId() == jobId) {
1133 return job.getJob();
1134 }
1135 }
1136 return null;
1137 }
1138 }
1139
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001140 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001141 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001142 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
1143 for (int i=0; i<jobsForUser.size(); i++) {
1144 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001145 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001146 }
Christopher Tate7060b042014-06-09 19:50:00 -07001147 }
1148 }
1149
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001150 private void cancelJobsForNonExistentUsers() {
1151 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
1152 synchronized (mLock) {
1153 mJobs.removeJobsOfNonUsers(umi.getUserIds());
1154 }
1155 }
1156
Makoto Onukie7b96182017-08-30 14:53:16 -07001157 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
1158 if ("android".equals(pkgName)) {
1159 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
1160 return;
1161 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001162 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001163 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1164 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1165 final JobStatus job = jobsForUid.get(i);
1166 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001167 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001168 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001169 }
1170 }
1171 }
1172
Christopher Tate7060b042014-06-09 19:50:00 -07001173 /**
1174 * Entry point from client to cancel all jobs originating from their uid.
1175 * This will remove the job from the master list, and cancel the job if it was staged for
1176 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -07001177 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -08001178 *
Christopher Tate7060b042014-06-09 19:50:00 -07001179 */
Christopher Tate8c67d122017-09-29 16:54:26 -07001180 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -07001181 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001182 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -07001183 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -07001184 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001185
1186 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001187 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001188 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1189 for (int i=0; i<jobsForUid.size(); i++) {
1190 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001191 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -07001192 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001193 }
Christopher Tate7060b042014-06-09 19:50:00 -07001194 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001195 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -07001196 }
1197
1198 /**
1199 * Entry point from client to cancel the job corresponding to the jobId provided.
1200 * This will remove the job from the master list, and cancel the job if it was staged for
1201 * execution or being executed.
1202 * @param uid Uid of the calling client.
1203 * @param jobId Id of the job, provided at schedule-time.
1204 */
Makoto Onukid2bfec62018-01-12 13:58:01 -08001205 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -07001206 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001207 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001208 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001209 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001210 cancelJobImplLocked(toCancel, null,
1211 "cancel() called by app, callingUid=" + callingUid
1212 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001213 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001214 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001215 }
1216 }
1217
Christopher Tate16032042018-10-05 15:53:50 -07001218 /**
1219 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob}
1220 * is null, the cancelled job is removed outright from the system. If
1221 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
1222 * currently scheduled jobs.
1223 */
Dianne Hackborn729a3282017-06-09 16:06:01 -07001224 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001225 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
Christopher Tate1b1fca82019-08-14 16:59:33 -07001226 cancelled.unprepareLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001227 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1228 // Remove from pending queue.
1229 if (mPendingJobs.remove(cancelled)) {
1230 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001231 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001232 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001233 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Christopher Tate16032042018-10-05 15:53:50 -07001234 // If this is a replacement, bring in the new version of the job
1235 if (incomingJob != null) {
1236 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
1237 startTrackingJobLocked(incomingJob, cancelled);
1238 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001239 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001240 }
1241
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001242 void updateUidState(int uid, int procState) {
1243 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001244 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1245 // Only use this if we are exactly the top app. All others can live
1246 // with just the foreground priority. This means that persistent processes
1247 // can never be the top app priority... that is fine.
1248 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001249 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
1250 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001251 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001252 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001253 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001254 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001255 }
1256 }
1257 }
1258
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001259 @Override
1260 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001261 synchronized (mLock) {
Makoto Onuki0f6499c2018-12-14 16:34:59 -08001262 if (DEBUG) {
1263 Slog.d(TAG, "Doze state changed: " + deviceIdle);
1264 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001265 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001266 // When becoming idle, make sure no jobs are actively running,
1267 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001268 for (int i=0; i<mActiveServices.size(); i++) {
1269 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001270 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001271 if (executing != null
1272 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001273 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1274 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001275 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001276 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001277 } else {
1278 // When coming out of idle, allow thing to start back up.
1279 if (mReadyToRock) {
1280 if (mLocalDeviceIdleController != null) {
1281 if (!mReportedActive) {
1282 mReportedActive = true;
1283 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001284 }
1285 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001286 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001287 }
1288 }
1289 }
1290 }
1291
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001292 @Override
1293 public void onRestrictedBucketChanged(List<JobStatus> jobs) {
1294 final int len = jobs.size();
1295 if (len == 0) {
1296 Slog.wtf(TAG, "onRestrictedBucketChanged called with no jobs");
1297 return;
1298 }
1299 synchronized (mLock) {
1300 for (int i = 0; i < len; ++i) {
1301 JobStatus js = jobs.get(i);
1302 for (int j = mRestrictiveControllers.size() - 1; j >= 0; --j) {
1303 // Effective standby bucket can change after this in some situations so use
1304 // the real bucket so that the job is tracked by the controllers.
1305 if (js.getStandbyBucket() == RESTRICTED_INDEX) {
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001306 mRestrictiveControllers.get(j).startTrackingRestrictedJobLocked(js);
1307 } else {
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001308 mRestrictiveControllers.get(j).stopTrackingRestrictedJobLocked(js);
1309 }
1310 }
1311 }
1312 }
1313 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1314 }
1315
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001316 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001317 // active is true if pending queue contains jobs OR some job is running.
1318 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001319 if (mPendingJobs.size() <= 0) {
1320 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001321 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001322 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001323 if (job != null
1324 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001325 && !job.dozeWhitelisted
1326 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001327 // We will report active if we have a job running and it is not an exception
1328 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001329 active = true;
1330 break;
1331 }
1332 }
1333 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001334
1335 if (mReportedActive != active) {
1336 mReportedActive = active;
1337 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001338 mLocalDeviceIdleController.setJobsActive(active);
1339 }
1340 }
1341 }
1342
Christopher Tated117b292018-01-05 17:32:36 -08001343 void reportAppUsage(String packageName, int userId) {
1344 // This app just transitioned into interactive use or near equivalent, so we should
1345 // take a look at its job state for feedback purposes.
1346 }
1347
Christopher Tate7060b042014-06-09 19:50:00 -07001348 /**
1349 * Initializes the system service.
1350 * <p>
1351 * Subclasses must define a single argument constructor that accepts the context
1352 * and passes it to super.
1353 * </p>
1354 *
1355 * @param context The system server context.
1356 */
1357 public JobSchedulerService(Context context) {
1358 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001359
1360 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Daulet Zhanguzin489e9c32019-12-30 12:45:49 +00001361 mActivityManagerInternal = Objects.requireNonNull(
Makoto Onuki15407842018-01-19 14:23:11 -08001362 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001363
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001364 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001365 mConstants = new Constants();
1366 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001367 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001368
Makoto Onuki714f97d2018-12-05 11:18:13 -08001369 mConcurrencyManager = new JobConcurrencyManager(this);
1370
Christopher Tatea732f012017-10-26 17:26:53 -07001371 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001372 mStandbyTracker = new StandbyTracker();
1373 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
Kweku Adams9239a1a2019-11-12 12:06:37 -08001374 mQuotaTracker = new CountQuotaTracker(context, Categorizer.SINGLE_CATEGORIZER);
1375 mQuotaTracker.setCountLimit(Category.SINGLE_CATEGORY,
1376 mConstants.API_QUOTA_SCHEDULE_COUNT,
1377 mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
Christopher Tateb909c4d52019-10-21 12:50:37 -07001378
Kweku Adamsc6a9b342020-01-08 18:37:26 -08001379 mAppStandbyInternal = LocalServices.getService(AppStandbyInternal.class);
1380 mAppStandbyInternal.addListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001381
1382 // The job store needs to call back
1383 publishLocalService(JobSchedulerInternal.class, new LocalService());
1384
1385 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001386 mJobs = JobStore.initAndGet(this);
1387
Christopher Tate7060b042014-06-09 19:50:00 -07001388 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001389 mControllers = new ArrayList<StateController>();
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001390 final ConnectivityController connectivityController = new ConnectivityController(this);
1391 mControllers.add(connectivityController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001392 mControllers.add(new TimeController(this));
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001393 final IdleController idleController = new IdleController(this);
1394 mControllers.add(idleController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001395 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001396 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001397 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001398 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001399 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001400 mControllers.add(new ContentObserverController(this));
1401 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001402 mControllers.add(mDeviceIdleJobsController);
Kweku Adamsb4b8b9c2019-06-13 18:37:51 -07001403 mQuotaController = new QuotaController(this);
1404 mControllers.add(mQuotaController);
Christopher Tate616541d2017-07-26 14:27:38 -07001405
Kweku Adamsae9d6be2020-01-14 16:10:14 -08001406 mRestrictiveControllers = new ArrayList<>();
1407 mRestrictiveControllers.add(mBatteryController);
1408 mRestrictiveControllers.add(connectivityController);
1409 mRestrictiveControllers.add(idleController);
1410
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02001411 // Create restrictions
1412 mJobRestrictions = new ArrayList<>();
1413 mJobRestrictions.add(new ThermalStatusRestriction(this));
1414
Suprabh Shukla08126472020-02-04 20:46:35 -08001415 mSystemGalleryPackage = Objects.requireNonNull(
1416 context.getString(R.string.config_systemGallery));
1417
Christopher Tate616541d2017-07-26 14:27:38 -07001418 // If the job store determined that it can't yet reschedule persisted jobs,
1419 // we need to start watching the clock.
1420 if (!mJobs.jobTimesInflatedValid()) {
1421 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1422 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1423 }
Christopher Tate7060b042014-06-09 19:50:00 -07001424 }
1425
Christopher Tate616541d2017-07-26 14:27:38 -07001426 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1427 @Override
1428 public void onReceive(Context context, Intent intent) {
1429 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1430 // When we reach clock sanity, recalculate the temporal windows
1431 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001432 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001433 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1434
1435 // We've done our job now, so stop watching the time.
1436 context.unregisterReceiver(this);
1437
1438 // And kick off the work to update the affected jobs, using a secondary
1439 // thread instead of chugging away here on the main looper thread.
1440 FgThread.getHandler().post(mJobTimeUpdater);
1441 }
1442 }
1443 }
1444 };
1445
1446 private final Runnable mJobTimeUpdater = () -> {
1447 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1448 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1449 synchronized (mLock) {
1450 // Note: we intentionally both look up the existing affected jobs and replace them
1451 // with recalculated ones inside the same lock lifetime.
1452 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1453
1454 // Now, at each position [i], we have both the existing JobStatus
1455 // and the one that replaces it.
1456 final int N = toAdd.size();
1457 for (int i = 0; i < N; i++) {
1458 final JobStatus oldJob = toRemove.get(i);
1459 final JobStatus newJob = toAdd.get(i);
1460 if (DEBUG) {
1461 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1462 }
1463 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1464 }
1465 }
1466 };
1467
Christopher Tate7060b042014-06-09 19:50:00 -07001468 @Override
1469 public void onStart() {
1470 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1471 }
1472
1473 @Override
1474 public void onBootPhase(int phase) {
1475 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001476 mConstantsObserver.start(getContext().getContentResolver());
Kweku Adams4e774a32019-04-24 18:01:29 -07001477 for (StateController controller : mControllers) {
1478 controller.onSystemServicesReady();
1479 }
Makoto Onuki15407842018-01-19 14:23:11 -08001480
Daulet Zhanguzin489e9c32019-12-30 12:45:49 +00001481 mAppStateTracker = Objects.requireNonNull(
Makoto Onukie4918212018-02-06 11:30:15 -08001482 LocalServices.getService(AppStateTracker.class));
Makoto Onuki15407842018-01-19 14:23:11 -08001483
Shreyas Basarge5db09082016-01-07 13:38:29 +00001484 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001485 final IntentFilter filter = new IntentFilter();
1486 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1487 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001488 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1489 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001490 filter.addDataScheme("package");
1491 getContext().registerReceiverAsUser(
1492 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1493 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1494 getContext().registerReceiverAsUser(
1495 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001496 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001497 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001498 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001499 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1500 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001501 } catch (RemoteException e) {
1502 // ignored; both services live in system_server
1503 }
Makoto Onukic279f2b2019-01-09 13:11:37 -08001504
1505 mConcurrencyManager.onSystemReady();
1506
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001507 // Remove any jobs that are not associated with any of the current users.
1508 cancelJobsForNonExistentUsers();
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02001509
1510 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
1511 mJobRestrictions.get(i).onSystemServicesReady();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001512 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001513 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001514 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001515 // Let's go!
1516 mReadyToRock = true;
1517 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1518 BatteryStats.SERVICE_NAME));
Makoto Onukieaf7eed2019-08-12 15:27:15 -07001519 mLocalDeviceIdleController =
1520 LocalServices.getService(DeviceIdleInternal.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001521 // Create the "runners".
1522 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1523 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001524 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001525 getContext().getMainLooper()));
1526 }
1527 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001528 mJobs.forEachJob((job) -> {
1529 for (int controller = 0; controller < mControllers.size(); controller++) {
1530 final StateController sc = mControllers.get(controller);
1531 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001532 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001533 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001534 // GO GO GO!
1535 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1536 }
Christopher Tate7060b042014-06-09 19:50:00 -07001537 }
1538 }
1539
1540 /**
1541 * Called when we have a job status object that we need to insert in our
1542 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1543 * about.
1544 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001545 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1546 if (!jobStatus.isPreparedLocked()) {
1547 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1548 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001549 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001550 final boolean update = mJobs.add(jobStatus);
1551 if (mReadyToRock) {
1552 for (int i = 0; i < mControllers.size(); i++) {
1553 StateController controller = mControllers.get(i);
1554 if (update) {
1555 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001556 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001557 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001558 }
Christopher Tate7060b042014-06-09 19:50:00 -07001559 }
1560 }
1561
1562 /**
Kweku Adams8843a2812019-07-02 15:30:23 -07001563 * Called when we want to remove a JobStatus object that we've finished executing.
1564 * @return true if the job was removed.
Christopher Tate7060b042014-06-09 19:50:00 -07001565 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001566 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Kweku Adams8843a2812019-07-02 15:30:23 -07001567 boolean removeFromPersisted) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001568 // Deal with any remaining work items in the old job.
Christopher Tate1b1fca82019-08-14 16:59:33 -07001569 jobStatus.stopTrackingJobLocked(incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001570
1571 // Remove from store as well as controllers.
Kweku Adams8843a2812019-07-02 15:30:23 -07001572 final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001573 if (removed && mReadyToRock) {
1574 for (int i=0; i<mControllers.size(); i++) {
1575 StateController controller = mControllers.get(i);
1576 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001577 }
1578 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001579 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001580 }
1581
Dianne Hackborn729a3282017-06-09 16:06:01 -07001582 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001583 for (int i=0; i<mActiveServices.size(); i++) {
1584 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001585 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001586 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001587 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001588 return true;
1589 }
1590 }
1591 return false;
1592 }
1593
1594 /**
1595 * @param job JobStatus we are querying against.
1596 * @return Whether or not the job represented by the status object is currently being run or
1597 * is pending.
1598 */
1599 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001600 for (int i=0; i<mActiveServices.size(); i++) {
1601 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001602 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001603 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1604 return true;
1605 }
1606 }
1607 return false;
1608 }
1609
Dianne Hackborn807de782016-04-07 17:54:41 -07001610 void noteJobsPending(List<JobStatus> jobs) {
1611 for (int i = jobs.size() - 1; i >= 0; i--) {
1612 JobStatus job = jobs.get(i);
1613 mJobPackageTracker.notePending(job);
1614 }
1615 }
1616
1617 void noteJobsNonpending(List<JobStatus> jobs) {
1618 for (int i = jobs.size() - 1; i >= 0; i--) {
1619 JobStatus job = jobs.get(i);
1620 mJobPackageTracker.noteNonpending(job);
1621 }
1622 }
1623
Christopher Tate7060b042014-06-09 19:50:00 -07001624 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001625 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1626 * specify an override deadline on a failed job (the failed job will run even though it's not
1627 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
Makoto Onukic279f2b2019-01-09 13:11:37 -08001628 * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001629 *
Christopher Tate7060b042014-06-09 19:50:00 -07001630 * @param failureToReschedule Provided job status that we will reschedule.
1631 * @return A newly instantiated JobStatus with the same constraints as the last job except
1632 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001633 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001634 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001635 */
Kweku Adamsc9793092019-04-30 11:54:48 -07001636 @VisibleForTesting
1637 JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001638 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001639 final JobInfo job = failureToReschedule.getJob();
1640
1641 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001642 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1643 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001644
1645 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001646 case JobInfo.BACKOFF_POLICY_LINEAR: {
1647 long backoff = initialBackoffMillis;
1648 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1649 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1650 }
1651 delayMillis = backoff * backoffAttempts;
1652 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001653 default:
1654 if (DEBUG) {
1655 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1656 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001657 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1658 long backoff = initialBackoffMillis;
1659 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1660 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1661 }
1662 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1663 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001664 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001665 delayMillis =
1666 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Kweku Adams6c5dc642019-07-25 10:29:38 -07001667 JobStatus newJob = new JobStatus(failureToReschedule,
Christopher Tatea732f012017-10-26 17:26:53 -07001668 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001669 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001670 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Kweku Adamsc9793092019-04-30 11:54:48 -07001671 if (job.isPeriodic()) {
1672 newJob.setOriginalLatestRunTimeElapsed(
1673 failureToReschedule.getOriginalLatestRunTimeElapsed());
1674 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001675 for (int ic=0; ic<mControllers.size(); ic++) {
1676 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001677 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001678 }
1679 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001680 }
1681
1682 /**
Kweku Adamsb6722782019-05-01 14:46:05 -07001683 * Maximum time buffer in which JobScheduler will try to optimize periodic job scheduling. This
1684 * does not cause a job's period to be larger than requested (eg: if the requested period is
1685 * shorter than this buffer). This is used to put a limit on when JobScheduler will intervene
1686 * and try to optimize scheduling if the current job finished less than this amount of time to
1687 * the start of the next period
1688 */
1689 private static final long PERIODIC_JOB_WINDOW_BUFFER = 30 * MINUTE_IN_MILLIS;
1690
Kweku Adams966e72b2019-05-30 14:45:39 -07001691 /** The maximum period a periodic job can have. Anything higher will be clamped down to this. */
1692 public static final long MAX_ALLOWED_PERIOD_MS = 365 * 24 * 60 * 60 * 1000L;
1693
Kweku Adamsb6722782019-05-01 14:46:05 -07001694 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001695 * Called after a periodic has executed so we can reschedule it. We take the last execution
1696 * time of the job to be the time of completion (i.e. the time at which this function is
1697 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001698 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001699 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1700 * to underscheduling at least, rather than if we had taken the last execution time to be the
1701 * start of the execution.
Kweku Adams6c5dc642019-07-25 10:29:38 -07001702 *
Christopher Tate7060b042014-06-09 19:50:00 -07001703 * @return A new job representing the execution criteria for this instantiation of the
1704 * recurring job.
1705 */
Kweku Adamsc9793092019-04-30 11:54:48 -07001706 @VisibleForTesting
1707 JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001708 final long elapsedNow = sElapsedRealtimeClock.millis();
Kweku Adamsc9793092019-04-30 11:54:48 -07001709 final long newLatestRuntimeElapsed;
Kweku Adams966e72b2019-05-30 14:45:39 -07001710 // Make sure period is in the interval [min_possible_period, max_possible_period].
1711 final long period = Math.max(JobInfo.getMinPeriodMillis(),
1712 Math.min(MAX_ALLOWED_PERIOD_MS, periodicToReschedule.getJob().getIntervalMillis()));
1713 // Make sure flex is in the interval [min_possible_flex, period].
1714 final long flex = Math.max(JobInfo.getMinFlexMillis(),
1715 Math.min(period, periodicToReschedule.getJob().getFlexMillis()));
Kweku Adamsb6722782019-05-01 14:46:05 -07001716 long rescheduleBuffer = 0;
Matthew Williams1bde39a2015-10-07 14:29:30 -07001717
Kweku Adams966e72b2019-05-30 14:45:39 -07001718 long olrte = periodicToReschedule.getOriginalLatestRunTimeElapsed();
1719 if (olrte < 0 || olrte == JobStatus.NO_LATEST_RUNTIME) {
1720 Slog.wtf(TAG, "Invalid periodic job original latest run time: " + olrte);
1721 olrte = elapsedNow;
1722 }
1723 final long latestRunTimeElapsed = olrte;
1724
Kweku Adamsb6722782019-05-01 14:46:05 -07001725 final long diffMs = Math.abs(elapsedNow - latestRunTimeElapsed);
Kweku Adamsc9793092019-04-30 11:54:48 -07001726 if (elapsedNow > latestRunTimeElapsed) {
1727 // The job ran past its expected run window. Have it count towards the current window
1728 // and schedule a new job for the next window.
1729 if (DEBUG) {
1730 Slog.i(TAG, "Periodic job ran after its intended window.");
1731 }
Kweku Adams966e72b2019-05-30 14:45:39 -07001732 long numSkippedWindows = (diffMs / period) + 1; // +1 to include original window
Kweku Adamsb6722782019-05-01 14:46:05 -07001733 if (period != flex && diffMs > Math.min(PERIODIC_JOB_WINDOW_BUFFER,
1734 (period - flex) / 2)) {
Kweku Adamsc9793092019-04-30 11:54:48 -07001735 if (DEBUG) {
1736 Slog.d(TAG, "Custom flex job ran too close to next window.");
1737 }
1738 // For custom flex periods, if the job was run too close to the next window,
1739 // skip the next window and schedule for the following one.
1740 numSkippedWindows += 1;
1741 }
1742 newLatestRuntimeElapsed = latestRunTimeElapsed + (period * numSkippedWindows);
1743 } else {
1744 newLatestRuntimeElapsed = latestRunTimeElapsed + period;
Kweku Adamsb6722782019-05-01 14:46:05 -07001745 if (diffMs < PERIODIC_JOB_WINDOW_BUFFER && diffMs < period / 6) {
1746 // Add a little buffer to the start of the next window so the job doesn't run
1747 // too soon after this completed one.
1748 rescheduleBuffer = Math.min(PERIODIC_JOB_WINDOW_BUFFER, period / 6 - diffMs);
1749 }
Matthew Williams1bde39a2015-10-07 14:29:30 -07001750 }
Kweku Adamsc9793092019-04-30 11:54:48 -07001751
Kweku Adams966e72b2019-05-30 14:45:39 -07001752 if (newLatestRuntimeElapsed < elapsedNow) {
1753 Slog.wtf(TAG, "Rescheduling calculated latest runtime in the past: "
1754 + newLatestRuntimeElapsed);
Kweku Adams6c5dc642019-07-25 10:29:38 -07001755 return new JobStatus(periodicToReschedule,
Kweku Adams966e72b2019-05-30 14:45:39 -07001756 elapsedNow + period - flex, elapsedNow + period,
1757 0 /* backoffAttempt */,
1758 sSystemClock.millis() /* lastSuccessfulRunTime */,
1759 periodicToReschedule.getLastFailedRunTime());
1760 }
1761
Kweku Adamsb6722782019-05-01 14:46:05 -07001762 final long newEarliestRunTimeElapsed = newLatestRuntimeElapsed
1763 - Math.min(flex, period - rescheduleBuffer);
Christopher Tate7060b042014-06-09 19:50:00 -07001764
1765 if (DEBUG) {
1766 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
Kweku Adamsc9793092019-04-30 11:54:48 -07001767 newEarliestRunTimeElapsed / 1000 + ", " + newLatestRuntimeElapsed / 1000
1768 + "]s");
Christopher Tate7060b042014-06-09 19:50:00 -07001769 }
Kweku Adams6c5dc642019-07-25 10:29:38 -07001770 return new JobStatus(periodicToReschedule,
Christopher Tatea732f012017-10-26 17:26:53 -07001771 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1772 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001773 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001774 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001775 }
1776
1777 // JobCompletedListener implementations.
1778
1779 /**
1780 * A job just finished executing. We fetch the
1781 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001782 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001783 * @param jobStatus Completed job.
1784 * @param needsReschedule Whether the implementing class should reschedule this job.
1785 */
1786 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001787 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001788 if (DEBUG) {
1789 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1790 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001791
1792 // If the job wants to be rescheduled, we first need to make the next upcoming
1793 // job so we can transfer any appropriate state over from the previous job when
1794 // we stop it.
1795 final JobStatus rescheduledJob = needsReschedule
1796 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1797
Shreyas Basarge73f10252016-02-11 17:06:13 +00001798 // Do not write back immediately if this is a periodic job. The job may get lost if system
1799 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001800 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001801 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001802 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001803 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001804 // We still want to check for jobs to execute, because this job may have
1805 // scheduled a new job under the same job id, and now we can run it.
1806 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001807 return;
1808 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001809
1810 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001811 try {
Christopher Tate1b1fca82019-08-14 16:59:33 -07001812 rescheduledJob.prepareLocked();
Dianne Hackborna47223f2017-03-30 13:49:13 -07001813 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001814 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001815 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001816 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001817 } else if (jobStatus.getJob().isPeriodic()) {
1818 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001819 try {
Christopher Tate1b1fca82019-08-14 16:59:33 -07001820 rescheduledPeriodic.prepareLocked();
Dianne Hackborna47223f2017-03-30 13:49:13 -07001821 } catch (SecurityException e) {
1822 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1823 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001824 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001825 }
Christopher Tate1b1fca82019-08-14 16:59:33 -07001826 jobStatus.unprepareLocked();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001827 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001828 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001829 }
1830
1831 // StateChangedListener implementations.
1832
1833 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001834 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1835 * some controller's state has changed, so as to run through the list of jobs and start/stop
1836 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001837 */
1838 @Override
1839 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001840 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001841 }
1842
1843 @Override
1844 public void onRunJobNow(JobStatus jobStatus) {
1845 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1846 }
1847
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001848 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001849
1850 public JobHandler(Looper looper) {
1851 super(looper);
1852 }
1853
1854 @Override
1855 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001856 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001857 if (!mReadyToRock) {
1858 return;
1859 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001860 switch (message.what) {
1861 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001862 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001863 // runNow can be null, which is a controller's way of indicating that its
1864 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001865 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001866 mJobPackageTracker.notePending(runNow);
Kweku Adamscdc2c992020-03-13 13:24:53 -07001867 addOrderedItem(mPendingJobs, runNow, sPendingJobComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001868 } else {
1869 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001870 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001871 } break;
1872 case MSG_CHECK_JOB:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08001873 if (DEBUG) {
1874 Slog.d(TAG, "MSG_CHECK_JOB");
1875 }
Kulwinder Singh9557fe82018-08-31 13:14:03 +05301876 removeMessages(MSG_CHECK_JOB);
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001877 if (mReportedActive) {
1878 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001879 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001880 } else {
1881 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001882 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001883 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001884 break;
1885 case MSG_CHECK_JOB_GREEDY:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08001886 if (DEBUG) {
1887 Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
1888 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001889 queueReadyJobsForExecutionLocked();
1890 break;
1891 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001892 cancelJobImplLocked((JobStatus) message.obj, null,
1893 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001894 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001895
1896 case MSG_UID_STATE_CHANGED: {
1897 final int uid = message.arg1;
1898 final int procState = message.arg2;
1899 updateUidState(uid, procState);
1900 break;
1901 }
1902 case MSG_UID_GONE: {
1903 final int uid = message.arg1;
1904 final boolean disabled = message.arg2 != 0;
1905 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1906 if (disabled) {
1907 cancelJobsForUid(uid, "uid gone");
1908 }
1909 synchronized (mLock) {
1910 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1911 }
1912 break;
1913 }
1914 case MSG_UID_ACTIVE: {
1915 final int uid = message.arg1;
1916 synchronized (mLock) {
1917 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1918 }
1919 break;
1920 }
1921 case MSG_UID_IDLE: {
1922 final int uid = message.arg1;
1923 final boolean disabled = message.arg2 != 0;
1924 if (disabled) {
1925 cancelJobsForUid(uid, "app uid idle");
1926 }
1927 synchronized (mLock) {
1928 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1929 }
1930 break;
1931 }
1932
Matthew Williams75fc5252014-09-02 16:17:53 -07001933 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001934 maybeRunPendingJobsLocked();
1935 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
Christopher Tate7060b042014-06-09 19:50:00 -07001936 }
1937 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001938 }
Christopher Tate7060b042014-06-09 19:50:00 -07001939
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02001940 /**
1941 * Check if a job is restricted by any of the declared {@link JobRestriction}s.
1942 * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not
1943 * be restricted, thus we won't even perform the check, but simply return null early.
1944 *
1945 * @param job to be checked
1946 * @return the first {@link JobRestriction} restricting the given job that has been found; null
1947 * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP}
1948 * or higher.
1949 */
1950 private JobRestriction checkIfRestricted(JobStatus job) {
1951 if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) {
1952 // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted
1953 return null;
1954 }
1955 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
1956 final JobRestriction restriction = mJobRestrictions.get(i);
1957 if (restriction.isJobRestricted(job)) {
1958 return restriction;
1959 }
1960 }
1961 return null;
Wei Wang8c0c3c12018-11-14 14:56:52 -08001962 }
1963
Dianne Hackborn6d068262017-05-16 13:14:37 -07001964 private void stopNonReadyActiveJobsLocked() {
1965 for (int i=0; i<mActiveServices.size(); i++) {
1966 JobServiceContext serviceContext = mActiveServices.get(i);
1967 final JobStatus running = serviceContext.getRunningJobLocked();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001968 if (running == null) {
1969 continue;
1970 }
1971 if (!running.isReady()) {
Kweku Adams338ce0b2020-02-25 14:46:59 -08001972 // If a restricted job doesn't have dynamic constraints satisfied, assume that's
1973 // the reason the job is being stopped, instead of because of other constraints
1974 // not being satisfied.
1975 if (running.getEffectiveStandbyBucket() == RESTRICTED_INDEX
1976 && !running.areDynamicConstraintsSatisfied()) {
1977 serviceContext.cancelExecutingJobLocked(
1978 JobParameters.REASON_RESTRICTED_BUCKET,
1979 "cancelled due to restricted bucket");
1980 } else {
1981 serviceContext.cancelExecutingJobLocked(
1982 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1983 "cancelled due to unsatisfied constraints");
1984 }
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02001985 } else {
1986 final JobRestriction restriction = checkIfRestricted(running);
1987 if (restriction != null) {
1988 final int reason = restriction.getReason();
1989 serviceContext.cancelExecutingJobLocked(reason,
Kweku Adams13289702020-02-19 17:28:55 -08001990 "restricted due to " + JobParameters.getReasonCodeDescription(reason));
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02001991 }
Dianne Hackborn6d068262017-05-16 13:14:37 -07001992 }
1993 }
1994 }
1995
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001996 /**
1997 * Run through list of jobs and execute all possible - at least one is expired so we do
1998 * as many as we can.
1999 */
2000 private void queueReadyJobsForExecutionLocked() {
2001 if (DEBUG) {
2002 Slog.d(TAG, "queuing all ready jobs for execution:");
2003 }
2004 noteJobsNonpending(mPendingJobs);
2005 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002006 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002007 mJobs.forEachJob(mReadyQueueFunctor);
2008 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08002009
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002010 if (DEBUG) {
2011 final int queuedJobs = mPendingJobs.size();
2012 if (queuedJobs == 0) {
2013 Slog.d(TAG, "No jobs pending.");
2014 } else {
2015 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08002016 }
2017 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002018 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002019
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002020 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Kweku Adams41cb3322019-06-26 14:42:26 -07002021 final ArrayList<JobStatus> newReadyJobs = new ArrayList<>();
Christopher Tate2f36fd62016-02-18 18:36:08 -08002022
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002023 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002024 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002025 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07002026 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002027 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07002028 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002029 newReadyJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002030 } else {
2031 evaluateControllerStatesLocked(job);
Christopher Tate7060b042014-06-09 19:50:00 -07002032 }
2033 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002034
2035 public void postProcess() {
Kweku Adams41cb3322019-06-26 14:42:26 -07002036 noteJobsPending(newReadyJobs);
2037 mPendingJobs.addAll(newReadyJobs);
2038 if (mPendingJobs.size() > 1) {
Kweku Adamscdc2c992020-03-13 13:24:53 -07002039 mPendingJobs.sort(sPendingJobComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002040 }
Kweku Adams41cb3322019-06-26 14:42:26 -07002041
2042 newReadyJobs.clear();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002043 }
2044 }
2045 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
2046
2047 /**
2048 * The state of at least one job has changed. Here is where we could enforce various
2049 * policies on when we want to execute jobs.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002050 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002051 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Kweku Adams41cb3322019-06-26 14:42:26 -07002052 int forceBatchedCount;
2053 int unbatchedCount;
2054 final List<JobStatus> runnableJobs = new ArrayList<>();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002055
2056 public MaybeReadyJobQueueFunctor() {
2057 reset();
2058 }
2059
2060 // Functor method invoked for each job via JobStore.forEachJob()
2061 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002062 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002063 if (isReadyToBeExecutedLocked(job)) {
2064 try {
2065 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
2066 job.getJob().getService().getPackageName())) {
2067 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
2068 + job.getJob().toString() + " -- package not allowed to start");
2069 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
2070 return;
2071 }
2072 } catch (RemoteException e) {
2073 }
Kweku Adams111b8222020-02-11 12:06:26 -08002074
2075 final boolean shouldForceBatchJob;
Kweku Adamsae9d6be2020-01-14 16:10:14 -08002076 // Restricted jobs must always be batched
Kweku Adams111b8222020-02-11 12:06:26 -08002077 if (job.getEffectiveStandbyBucket() == RESTRICTED_INDEX) {
2078 shouldForceBatchJob = true;
2079 } else if (job.getNumFailures() > 0) {
2080 shouldForceBatchJob = false;
2081 } else {
2082 final long nowElapsed = sElapsedRealtimeClock.millis();
2083 final boolean batchDelayExpired = job.getFirstForceBatchedTimeElapsed() > 0
2084 && nowElapsed - job.getFirstForceBatchedTimeElapsed()
2085 >= mConstants.MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS;
2086 shouldForceBatchJob =
2087 mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
2088 && job.getEffectiveStandbyBucket() != ACTIVE_INDEX
2089 && !batchDelayExpired;
2090 }
2091
2092 if (shouldForceBatchJob) {
Kweku Adams41cb3322019-06-26 14:42:26 -07002093 // Force batching non-ACTIVE jobs. Don't include them in the other counts.
2094 forceBatchedCount++;
2095 if (job.getFirstForceBatchedTimeElapsed() == 0) {
2096 job.setFirstForceBatchedTimeElapsed(sElapsedRealtimeClock.millis());
2097 }
2098 } else {
2099 unbatchedCount++;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002100 }
2101 runnableJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002102 } else {
2103 evaluateControllerStatesLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002104 }
2105 }
2106
2107 public void postProcess() {
Kweku Adams111b8222020-02-11 12:06:26 -08002108 if (unbatchedCount > 0
2109 || forceBatchedCount >= mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002110 if (DEBUG) {
2111 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
2112 }
2113 noteJobsPending(runnableJobs);
2114 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002115 if (mPendingJobs.size() > 1) {
Kweku Adamscdc2c992020-03-13 13:24:53 -07002116 mPendingJobs.sort(sPendingJobComparator);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002117 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002118 } else {
2119 if (DEBUG) {
2120 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
2121 }
2122 }
2123
2124 // Be ready for next time
2125 reset();
2126 }
2127
Kweku Adams41cb3322019-06-26 14:42:26 -07002128 @VisibleForTesting
2129 void reset() {
Kweku Adams41cb3322019-06-26 14:42:26 -07002130 forceBatchedCount = 0;
2131 unbatchedCount = 0;
2132 runnableJobs.clear();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002133 }
2134 }
2135 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
2136
2137 private void maybeQueueReadyJobsForExecutionLocked() {
2138 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
2139
2140 noteJobsNonpending(mPendingJobs);
2141 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002142 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002143 mJobs.forEachJob(mMaybeQueueFunctor);
2144 mMaybeQueueFunctor.postProcess();
2145 }
2146
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002147 /** Returns true if both the calling and source users for the job are started. */
2148 private boolean areUsersStartedLocked(final JobStatus job) {
2149 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
2150 if (job.getUserId() == job.getSourceUserId()) {
2151 return sourceStarted;
2152 }
2153 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
2154 }
2155
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002156 /**
2157 * Criteria for moving a job into the pending queue:
2158 * - It's ready.
2159 * - It's not pending.
2160 * - It's not already running on a JSC.
2161 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07002162 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002163 * - The component is enabled and runnable.
2164 */
Kweku Adams41cb3322019-06-26 14:42:26 -07002165 @VisibleForTesting
2166 boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002167 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002168
2169 if (DEBUG) {
2170 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2171 + " ready=" + jobReady);
2172 }
2173
2174 // This is a condition that is very likely to be false (most jobs that are
2175 // scheduled are sitting there, not ready yet) and very cheap to check (just
2176 // a few conditions on data in JobStatus).
2177 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08002178 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
2179 Slog.v(TAG, " NOT READY: " + job);
2180 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002181 return false;
2182 }
2183
2184 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002185
Kweku Adams8bd5edc2018-12-07 18:33:39 -08002186 final boolean userStarted = areUsersStartedLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002187
2188 if (DEBUG) {
2189 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002190 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002191 }
2192
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002193 // These are also fairly cheap to check, though they typically will not
2194 // be conditions we fail.
2195 if (!jobExists || !userStarted) {
2196 return false;
2197 }
2198
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02002199 if (checkIfRestricted(job) != null) {
Wei Wang8c0c3c12018-11-14 14:56:52 -08002200 return false;
2201 }
2202
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002203 final boolean jobPending = mPendingJobs.contains(job);
2204 final boolean jobActive = isCurrentlyActiveLocked(job);
2205
2206 if (DEBUG) {
2207 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2208 + " pending=" + jobPending + " active=" + jobActive);
2209 }
2210
2211 // These can be a little more expensive (especially jobActive, since we need to
2212 // go through the array of all potentially active jobs), so we are doing them
2213 // later... but still before checking with the package manager!
2214 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002215 return false;
2216 }
2217
Christopher Tate8e737852019-03-15 14:56:53 -07002218 // The expensive check: validate that the defined package+service is
Christopher Tatea732f012017-10-26 17:26:53 -07002219 // still present & viable.
Kweku Adams649e2fc2019-03-18 14:55:38 -07002220 return isComponentUsable(job);
2221 }
2222
2223 private boolean isComponentUsable(@NonNull JobStatus job) {
Christopher Tate8e737852019-03-15 14:56:53 -07002224 final ServiceInfo service;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002225 try {
Kweku Adams649e2fc2019-03-18 14:55:38 -07002226 // TODO: cache result until we're notified that something in the package changed.
Christopher Tate8e737852019-03-15 14:56:53 -07002227 service = AppGlobals.getPackageManager().getServiceInfo(
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002228 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
Christopher Tate8e737852019-03-15 14:56:53 -07002229 job.getUserId());
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002230 } catch (RemoteException e) {
Makoto Onuki0e1ce972019-10-09 12:51:55 -07002231 throw new RuntimeException(e);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002232 }
2233
Christopher Tate8e737852019-03-15 14:56:53 -07002234 if (service == null) {
2235 if (DEBUG) {
Kweku Adams649e2fc2019-03-18 14:55:38 -07002236 Slog.v(TAG, "isComponentUsable: " + job.toShortString()
Christopher Tate8e737852019-03-15 14:56:53 -07002237 + " component not present");
2238 }
2239 return false;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002240 }
2241
2242 // Everything else checked out so far, so this is the final yes/no check
Christopher Tate8e737852019-03-15 14:56:53 -07002243 final boolean appIsBad = mActivityManagerInternal.isAppBad(service.applicationInfo);
Kweku Adams649e2fc2019-03-18 14:55:38 -07002244 if (DEBUG && appIsBad) {
2245 Slog.i(TAG, "App is bad for " + job.toShortString() + " so not runnable");
Christopher Tate8e737852019-03-15 14:56:53 -07002246 }
2247 return !appIsBad;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002248 }
2249
Kweku Adams41cb3322019-06-26 14:42:26 -07002250 @VisibleForTesting
2251 void evaluateControllerStatesLocked(final JobStatus job) {
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002252 for (int c = mControllers.size() - 1; c >= 0; --c) {
2253 final StateController sc = mControllers.get(c);
2254 sc.evaluateStateLocked(job);
2255 }
2256 }
2257
2258 /**
2259 * Returns true if non-job constraint components are in place -- if job.isReady() returns true
2260 * and this method returns true, then the job is ready to be executed.
2261 */
2262 public boolean areComponentsInPlaceLocked(JobStatus job) {
2263 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
2264 // conditions.
2265
2266 final boolean jobExists = mJobs.containsJob(job);
2267 final boolean userStarted = areUsersStartedLocked(job);
2268
2269 if (DEBUG) {
2270 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2271 + " exists=" + jobExists + " userStarted=" + userStarted);
2272 }
2273
2274 // These are also fairly cheap to check, though they typically will not
2275 // be conditions we fail.
2276 if (!jobExists || !userStarted) {
2277 return false;
2278 }
2279
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02002280 if (checkIfRestricted(job) != null) {
Kweku Adams649e2fc2019-03-18 14:55:38 -07002281 return false;
2282 }
2283
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002284 // Job pending/active doesn't affect the readiness of a job.
2285
Kweku Adams649e2fc2019-03-18 14:55:38 -07002286 // The expensive check: validate that the defined package+service is
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002287 // still present & viable.
Kweku Adams649e2fc2019-03-18 14:55:38 -07002288 return isComponentUsable(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002289 }
2290
Kweku Adamsb4b8b9c2019-06-13 18:37:51 -07002291 /** Returns the maximum amount of time this job could run for. */
2292 public long getMaxJobExecutionTimeMs(JobStatus job) {
2293 synchronized (mLock) {
Kweku Adamsb4b8b9c2019-06-13 18:37:51 -07002294 return Math.min(mQuotaController.getMaxJobExecutionTimeMsLocked(job),
2295 JobServiceContext.EXECUTING_TIMESLICE_MILLIS);
2296 }
2297 }
2298
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002299 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002300 * Reconcile jobs in the pending queue against available execution contexts.
2301 * A controller can force a job into the pending queue even if it's already running, but
2302 * here is where we decide whether to actually execute it.
2303 */
Makoto Onukic279f2b2019-01-09 13:11:37 -08002304 void maybeRunPendingJobsLocked() {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002305 if (DEBUG) {
2306 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2307 }
Makoto Onuki714f97d2018-12-05 11:18:13 -08002308 mConcurrencyManager.assignJobsToContextsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002309 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002310 }
2311
Dianne Hackborn807de782016-04-07 17:54:41 -07002312 private int adjustJobPriority(int curPriority, JobStatus job) {
2313 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2314 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002315 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002316 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002317 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002318 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2319 }
2320 }
2321 return curPriority;
2322 }
2323
Makoto Onuki714f97d2018-12-05 11:18:13 -08002324 int evaluateJobPriorityLocked(JobStatus job) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002325 int priority = job.getPriority();
Makoto Onukiec8b14d2018-12-05 13:22:24 -08002326 if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002327 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002328 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002329 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2330 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002331 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002332 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002333 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002334 }
2335
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002336 final class LocalService implements JobSchedulerInternal {
2337
2338 /**
2339 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2340 * jobs are always considered pending.
2341 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002342 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002343 public List<JobInfo> getSystemScheduledPendingJobs() {
2344 synchronized (mLock) {
2345 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002346 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2347 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2348 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002349 }
2350 });
2351 return pendingJobs;
2352 }
2353 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002354
2355 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002356 public void cancelJobsForUid(int uid, String reason) {
2357 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2358 }
2359
2360 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002361 public void addBackingUpUid(int uid) {
2362 synchronized (mLock) {
2363 // No need to actually do anything here, since for a full backup the
2364 // activity manager will kill the process which will kill the job (and
2365 // cause it to restart, but now it can't run).
2366 mBackingUpUids.put(uid, uid);
2367 }
2368 }
2369
2370 @Override
2371 public void removeBackingUpUid(int uid) {
2372 synchronized (mLock) {
2373 mBackingUpUids.delete(uid);
2374 // If there are any jobs for this uid, we need to rebuild the pending list
2375 // in case they are now ready to run.
2376 if (mJobs.countJobsForUid(uid) > 0) {
2377 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2378 }
2379 }
2380 }
2381
2382 @Override
2383 public void clearAllBackingUpUids() {
2384 synchronized (mLock) {
2385 if (mBackingUpUids.size() > 0) {
2386 mBackingUpUids.clear();
2387 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2388 }
2389 }
2390 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002391
2392 @Override
Suprabh Shukla08126472020-02-04 20:46:35 -08002393 public String getMediaBackupPackage() {
2394 return mSystemGalleryPackage;
2395 }
2396
2397 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002398 public void reportAppUsage(String packageName, int userId) {
2399 JobSchedulerService.this.reportAppUsage(packageName, userId);
2400 }
2401
2402 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002403 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002404 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002405 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002406 }
2407 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002408 }
2409
Shreyas Basarge5db09082016-01-07 13:38:29 +00002410 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002411 * Tracking of app assignments to standby buckets
2412 */
2413 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002414
Christopher Tatea732f012017-10-26 17:26:53 -07002415 // AppIdleStateChangeListener interface for live updates
2416
2417 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002418 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002419 boolean idle, int bucket, int reason) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002420 // QuotaController handles this now.
Christopher Tatea732f012017-10-26 17:26:53 -07002421 }
2422
2423 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002424 public void onUserInteractionStarted(String packageName, int userId) {
2425 final int uid = mLocalPM.getPackageUid(packageName,
2426 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2427 if (uid < 0) {
2428 // Quietly ignore; the case is already logged elsewhere
2429 return;
2430 }
2431
Amith Yamasani977e11f2018-02-16 11:29:54 -08002432 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2433 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2434 // Too long ago, not worth logging
2435 sinceLast = 0L;
2436 }
Christopher Tated117b292018-01-05 17:32:36 -08002437 final DeferredJobCounter counter = new DeferredJobCounter();
2438 synchronized (mLock) {
2439 mJobs.forEachJobForSourceUid(uid, counter);
2440 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002441 if (counter.numDeferred() > 0 || sinceLast > 0) {
2442 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2443 (BatteryStatsInternal.class);
2444 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
Muhammad Qureshie9aec322020-01-28 10:27:18 -08002445 FrameworkStatsLog.write_non_chained(
2446 FrameworkStatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
Yangster-mac96353002018-09-05 11:18:55 -07002447 counter.numDeferred(), sinceLast);
Amith Yamasani977e11f2018-02-16 11:29:54 -08002448 }
Christopher Tated117b292018-01-05 17:32:36 -08002449 }
2450 }
2451
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002452 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002453 private int mDeferred = 0;
2454
2455 public int numDeferred() {
2456 return mDeferred;
2457 }
2458
2459 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002460 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002461 if (job.getWhenStandbyDeferred() > 0) {
2462 mDeferred++;
2463 }
2464 }
Christopher Tatea732f012017-10-26 17:26:53 -07002465 }
2466
2467 public static int standbyBucketToBucketIndex(int bucket) {
2468 // Normalize AppStandby constants to indices into our bookkeeping
Kweku Adamsae9d6be2020-01-14 16:10:14 -08002469 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) {
2470 return NEVER_INDEX;
2471 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_RARE) {
2472 return RESTRICTED_INDEX;
2473 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) {
2474 return RARE_INDEX;
2475 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) {
2476 return FREQUENT_INDEX;
2477 } else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
2478 return WORKING_INDEX;
2479 } else {
2480 return ACTIVE_INDEX;
2481 }
Christopher Tatea732f012017-10-26 17:26:53 -07002482 }
2483
Christopher Tated1aebb32018-01-31 13:24:14 -08002484 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002485 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2486 UsageStatsManagerInternal usageStats = LocalServices.getService(
2487 UsageStatsManagerInternal.class);
2488 int bucket = usageStats != null
2489 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2490 : 0;
2491
2492 bucket = standbyBucketToBucketIndex(bucket);
2493
2494 if (DEBUG_STANDBY) {
2495 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2496 }
2497 return bucket;
2498 }
2499
2500 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002501 * Binder stub trampoline implementation
2502 */
2503 final class JobSchedulerStub extends IJobScheduler.Stub {
2504 /** Cache determination of whether a given app can persist jobs
2505 * key is uid of the calling app; value is undetermined/true/false
2506 */
2507 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2508
2509 // Enforce that only the app itself (or shared uid participant) can schedule a
2510 // job that runs one of the app's services, as well as verifying that the
2511 // named service properly requires the BIND_JOB_SERVICE permission
2512 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002513 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002514 final ComponentName service = job.getService();
2515 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002516 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002517 PackageManager.MATCH_DIRECT_BOOT_AWARE
2518 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002519 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002520 if (si == null) {
2521 throw new IllegalArgumentException("No such service " + service);
2522 }
Christopher Tate7060b042014-06-09 19:50:00 -07002523 if (si.applicationInfo.uid != uid) {
2524 throw new IllegalArgumentException("uid " + uid +
2525 " cannot schedule job in " + service.getPackageName());
2526 }
2527 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2528 throw new IllegalArgumentException("Scheduled service " + service
2529 + " does not require android.permission.BIND_JOB_SERVICE permission");
2530 }
Christopher Tate5568f542014-06-18 13:53:31 -07002531 } catch (RemoteException e) {
2532 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002533 }
2534 }
2535
2536 private boolean canPersistJobs(int pid, int uid) {
2537 // If we get this far we're good to go; all we need to do now is check
2538 // whether the app is allowed to persist its scheduled work.
2539 final boolean canPersist;
2540 synchronized (mPersistCache) {
2541 Boolean cached = mPersistCache.get(uid);
2542 if (cached != null) {
2543 canPersist = cached.booleanValue();
2544 } else {
2545 // Persisting jobs is tantamount to running at boot, so we permit
2546 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2547 // permission
2548 int result = getContext().checkPermission(
2549 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2550 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2551 mPersistCache.put(uid, canPersist);
2552 }
2553 }
2554 return canPersist;
2555 }
2556
Makoto Onuki959acb52018-01-26 14:10:03 -08002557 private void validateJobFlags(JobInfo job, int callingUid) {
2558 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2559 getContext().enforceCallingOrSelfPermission(
2560 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2561 }
2562 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2563 if (callingUid != Process.SYSTEM_UID) {
2564 throw new SecurityException("Job has invalid flags");
2565 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002566 if (job.isPeriodic()) {
2567 Slog.wtf(TAG, "Periodic jobs mustn't have"
2568 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002569 }
2570 }
2571 }
2572
Christopher Tate7060b042014-06-09 19:50:00 -07002573 // IJobScheduler implementation
2574 @Override
2575 public int schedule(JobInfo job) throws RemoteException {
2576 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002577 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002578 }
2579 final int pid = Binder.getCallingPid();
2580 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002581 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002582
2583 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002584 if (job.isPersisted()) {
2585 if (!canPersistJobs(pid, uid)) {
2586 throw new IllegalArgumentException("Error: requested job be persisted without"
2587 + " holding RECEIVE_BOOT_COMPLETED permission.");
2588 }
2589 }
Christopher Tate7060b042014-06-09 19:50:00 -07002590
Makoto Onuki959acb52018-01-26 14:10:03 -08002591 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002592
Christopher Tate7060b042014-06-09 19:50:00 -07002593 long ident = Binder.clearCallingIdentity();
2594 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002595 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2596 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002597 } finally {
2598 Binder.restoreCallingIdentity(ident);
2599 }
2600 }
2601
2602 // IJobScheduler implementation
2603 @Override
2604 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2605 if (DEBUG) {
2606 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2607 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002608 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002609 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002610
2611 enforceValidJobRequest(uid, job);
2612 if (job.isPersisted()) {
2613 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2614 }
2615 if (work == null) {
2616 throw new NullPointerException("work is null");
2617 }
2618
Makoto Onuki959acb52018-01-26 14:10:03 -08002619 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002620
2621 long ident = Binder.clearCallingIdentity();
2622 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002623 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2624 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002625 } finally {
2626 Binder.restoreCallingIdentity(ident);
2627 }
2628 }
2629
2630 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002631 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002632 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002633 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002634 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002635 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002636 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002637 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002638
2639 if (packageName == null) {
2640 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002641 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002642
2643 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2644 android.Manifest.permission.UPDATE_DEVICE_STATS);
2645 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2646 throw new SecurityException("Caller uid " + callerUid
2647 + " not permitted to schedule jobs for other apps");
2648 }
2649
Makoto Onuki959acb52018-01-26 14:10:03 -08002650 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002651
Shreyas Basarge968ac752016-01-11 23:09:26 +00002652 long ident = Binder.clearCallingIdentity();
2653 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002654 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002655 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002656 } finally {
2657 Binder.restoreCallingIdentity(ident);
2658 }
2659 }
2660
2661 @Override
Kweku Adamsd1f4b902019-05-07 12:22:47 -07002662 public ParceledListSlice<JobInfo> getAllPendingJobs() throws RemoteException {
Christopher Tate7060b042014-06-09 19:50:00 -07002663 final int uid = Binder.getCallingUid();
2664
2665 long ident = Binder.clearCallingIdentity();
2666 try {
Kweku Adamsd1f4b902019-05-07 12:22:47 -07002667 return new ParceledListSlice<>(JobSchedulerService.this.getPendingJobs(uid));
Christopher Tate7060b042014-06-09 19:50:00 -07002668 } finally {
2669 Binder.restoreCallingIdentity(ident);
2670 }
2671 }
2672
2673 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002674 public JobInfo getPendingJob(int jobId) throws RemoteException {
2675 final int uid = Binder.getCallingUid();
2676
2677 long ident = Binder.clearCallingIdentity();
2678 try {
2679 return JobSchedulerService.this.getPendingJob(uid, jobId);
2680 } finally {
2681 Binder.restoreCallingIdentity(ident);
2682 }
2683 }
2684
2685 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002686 public void cancelAll() throws RemoteException {
2687 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002688 long ident = Binder.clearCallingIdentity();
2689 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002690 JobSchedulerService.this.cancelJobsForUid(uid,
2691 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002692 } finally {
2693 Binder.restoreCallingIdentity(ident);
2694 }
2695 }
2696
2697 @Override
2698 public void cancel(int jobId) throws RemoteException {
2699 final int uid = Binder.getCallingUid();
2700
2701 long ident = Binder.clearCallingIdentity();
2702 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002703 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002704 } finally {
2705 Binder.restoreCallingIdentity(ident);
2706 }
2707 }
2708
2709 /**
2710 * "dumpsys" infrastructure
2711 */
2712 @Override
2713 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002714 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002715
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002716 int filterUid = -1;
2717 boolean proto = false;
2718 if (!ArrayUtils.isEmpty(args)) {
2719 int opti = 0;
2720 while (opti < args.length) {
2721 String arg = args[opti];
2722 if ("-h".equals(arg)) {
2723 dumpHelp(pw);
2724 return;
2725 } else if ("-a".equals(arg)) {
2726 // Ignore, we always dump all.
2727 } else if ("--proto".equals(arg)) {
2728 proto = true;
2729 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2730 pw.println("Unknown option: " + arg);
2731 return;
2732 } else {
2733 break;
2734 }
2735 opti++;
2736 }
2737 if (opti < args.length) {
2738 String pkg = args[opti];
2739 try {
2740 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2741 PackageManager.MATCH_ANY_USER);
2742 } catch (NameNotFoundException ignored) {
2743 pw.println("Invalid package: " + pkg);
2744 return;
2745 }
2746 }
2747 }
2748
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002749 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07002750 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002751 if (proto) {
2752 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2753 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002754 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
2755 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002756 }
Christopher Tate7060b042014-06-09 19:50:00 -07002757 } finally {
2758 Binder.restoreCallingIdentity(identityToken);
2759 }
2760 }
Christopher Tate5d346052016-03-08 12:56:08 -08002761
2762 @Override
Makoto Onukif8f55022019-12-02 15:07:00 -08002763 public int handleShellCommand(@NonNull ParcelFileDescriptor in,
Makoto Onukib8472182019-11-13 12:03:22 -08002764 @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
2765 @NonNull String[] args) {
Makoto Onuki4c7073b2019-11-04 10:37:15 -08002766 return (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Makoto Onukib8472182019-11-13 12:03:22 -08002767 this, in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(),
2768 args);
Christopher Tate5d346052016-03-08 12:56:08 -08002769 }
Serik Beketayev75915d12018-08-01 16:56:59 -07002770
2771 /**
2772 * <b>For internal system user only!</b>
2773 * Returns a list of all currently-executing jobs.
2774 */
2775 @Override
2776 public List<JobInfo> getStartedJobs() {
2777 final int uid = Binder.getCallingUid();
2778 if (uid != Process.SYSTEM_UID) {
2779 throw new SecurityException(
2780 "getStartedJobs() is system internal use only.");
2781 }
2782
2783 final ArrayList<JobInfo> runningJobs;
2784
2785 synchronized (mLock) {
2786 runningJobs = new ArrayList<>(mActiveServices.size());
2787 for (JobServiceContext jsc : mActiveServices) {
2788 final JobStatus job = jsc.getRunningJobLocked();
2789 if (job != null) {
2790 runningJobs.add(job.getJob());
2791 }
2792 }
2793 }
2794
2795 return runningJobs;
2796 }
2797
2798 /**
2799 * <b>For internal system user only!</b>
2800 * Returns a snapshot of the state of all jobs known to the system.
2801 *
2802 * <p class="note">This is a slow operation, so it should be called sparingly.
2803 */
2804 @Override
Kweku Adamsd1f4b902019-05-07 12:22:47 -07002805 public ParceledListSlice<JobSnapshot> getAllJobSnapshots() {
Serik Beketayev75915d12018-08-01 16:56:59 -07002806 final int uid = Binder.getCallingUid();
2807 if (uid != Process.SYSTEM_UID) {
2808 throw new SecurityException(
2809 "getAllJobSnapshots() is system internal use only.");
2810 }
2811 synchronized (mLock) {
2812 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
2813 mJobs.forEachJob((job) -> snapshots.add(
2814 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
2815 isReadyToBeExecutedLocked(job))));
Kweku Adamsd1f4b902019-05-07 12:22:47 -07002816 return new ParceledListSlice<>(snapshots);
Serik Beketayev75915d12018-08-01 16:56:59 -07002817 }
2818 }
Kweku Adams9239a1a2019-11-12 12:06:37 -08002819 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002820
Christopher Tate5d346052016-03-08 12:56:08 -08002821 // Shell command infrastructure: run the given job immediately
Kweku Adamscdc2c992020-03-13 13:24:53 -07002822 int executeRunCommand(String pkgName, int userId, int jobId, boolean satisfied, boolean force) {
2823 Slog.d(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2824 + " " + jobId + " s=" + satisfied + " f=" + force);
Christopher Tate5d346052016-03-08 12:56:08 -08002825
2826 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002827 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2828 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002829 if (uid < 0) {
2830 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2831 }
2832
2833 synchronized (mLock) {
2834 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2835 if (js == null) {
2836 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2837 }
2838
Kweku Adamscdc2c992020-03-13 13:24:53 -07002839 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL
2840 : (satisfied ? JobStatus.OVERRIDE_SORTING : JobStatus.OVERRIDE_SOFT);
Kweku Adams7820b0d2020-03-02 15:28:36 -08002841
2842 // Re-evaluate constraints after the override is set in case one of the overridden
2843 // constraints was preventing another constraint from thinking it needed to update.
2844 for (int c = mControllers.size() - 1; c >= 0; --c) {
2845 mControllers.get(c).reevaluateStateLocked(uid);
2846 }
2847
Christopher Tate5d346052016-03-08 12:56:08 -08002848 if (!js.isConstraintsSatisfied()) {
Kweku Adamscdc2c992020-03-13 13:24:53 -07002849 js.overrideState = JobStatus.OVERRIDE_NONE;
Christopher Tate5d346052016-03-08 12:56:08 -08002850 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2851 }
2852
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002853 queueReadyJobsForExecutionLocked();
2854 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002855 }
2856 } catch (RemoteException e) {
2857 // can't happen
2858 }
2859 return 0;
2860 }
2861
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002862 // Shell command infrastructure: immediately timeout currently executing jobs
2863 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2864 boolean hasJobId, int jobId) {
2865 if (DEBUG) {
2866 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2867 }
2868
2869 synchronized (mLock) {
2870 boolean foundSome = false;
2871 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002872 final JobServiceContext jc = mActiveServices.get(i);
2873 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002874 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002875 foundSome = true;
2876 pw.print("Timing out: ");
2877 js.printUniqueId(pw);
2878 pw.print(" ");
2879 pw.println(js.getServiceComponent().flattenToShortString());
2880 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002881 }
2882 if (!foundSome) {
2883 pw.println("No matching executing jobs found.");
2884 }
2885 }
2886 return 0;
2887 }
2888
Christopher Tate8c67d122017-09-29 16:54:26 -07002889 // Shell command infrastructure: cancel a scheduled job
2890 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2891 boolean hasJobId, int jobId) {
2892 if (DEBUG) {
2893 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2894 }
2895
2896 int pkgUid = -1;
2897 try {
2898 IPackageManager pm = AppGlobals.getPackageManager();
2899 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2900 } catch (RemoteException e) { /* can't happen */ }
2901
2902 if (pkgUid < 0) {
2903 pw.println("Package " + pkgName + " not found.");
2904 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2905 }
2906
2907 if (!hasJobId) {
2908 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2909 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2910 pw.println("No matching jobs found.");
2911 }
2912 } else {
2913 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002914 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002915 pw.println("No matching job found.");
2916 }
2917 }
2918
2919 return 0;
2920 }
2921
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002922 void setMonitorBattery(boolean enabled) {
2923 synchronized (mLock) {
2924 if (mBatteryController != null) {
2925 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2926 }
2927 }
2928 }
2929
2930 int getBatterySeq() {
2931 synchronized (mLock) {
2932 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2933 }
2934 }
2935
2936 boolean getBatteryCharging() {
2937 synchronized (mLock) {
2938 return mBatteryController != null
2939 ? mBatteryController.getTracker().isOnStablePower() : false;
2940 }
2941 }
2942
2943 boolean getBatteryNotLow() {
2944 synchronized (mLock) {
2945 return mBatteryController != null
2946 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2947 }
2948 }
2949
Dianne Hackborn532ea262017-03-17 17:50:55 -07002950 int getStorageSeq() {
2951 synchronized (mLock) {
2952 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2953 }
2954 }
2955
2956 boolean getStorageNotLow() {
2957 synchronized (mLock) {
2958 return mStorageController != null
2959 ? mStorageController.getTracker().isStorageNotLow() : false;
2960 }
2961 }
2962
Christopher Tated1aebb32018-01-31 13:24:14 -08002963 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07002964 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2965 try {
2966 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2967 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2968 if (uid < 0) {
2969 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2970 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2971 }
2972
2973 synchronized (mLock) {
2974 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2975 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2976 if (js == null) {
2977 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2978 pw.print("/jid"); pw.print(jobId); pw.println(")");
2979 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2980 }
2981
2982 boolean printed = false;
2983 if (mPendingJobs.contains(js)) {
2984 pw.print("pending");
2985 printed = true;
2986 }
2987 if (isCurrentlyActiveLocked(js)) {
2988 if (printed) {
2989 pw.print(" ");
2990 }
2991 printed = true;
2992 pw.println("active");
2993 }
2994 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2995 if (printed) {
2996 pw.print(" ");
2997 }
2998 printed = true;
2999 pw.println("user-stopped");
3000 }
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003001 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
3002 if (printed) {
3003 pw.print(" ");
3004 }
3005 printed = true;
3006 pw.println("source-user-stopped");
3007 }
Dianne Hackborn6d068262017-05-16 13:14:37 -07003008 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
3009 if (printed) {
3010 pw.print(" ");
3011 }
3012 printed = true;
3013 pw.println("backing-up");
3014 }
3015 boolean componentPresent = false;
3016 try {
3017 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3018 js.getServiceComponent(),
3019 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3020 js.getUserId()) != null);
3021 } catch (RemoteException e) {
3022 }
3023 if (!componentPresent) {
3024 if (printed) {
3025 pw.print(" ");
3026 }
3027 printed = true;
3028 pw.println("no-component");
3029 }
3030 if (js.isReady()) {
3031 if (printed) {
3032 pw.print(" ");
3033 }
3034 printed = true;
3035 pw.println("ready");
3036 }
3037 if (!printed) {
3038 pw.print("waiting");
3039 }
3040 pw.println();
3041 }
3042 } catch (RemoteException e) {
3043 // can't happen
3044 }
3045 return 0;
3046 }
3047
Kweku Adams87a4cd52020-02-24 17:20:04 -08003048 void resetExecutionQuota(@NonNull String pkgName, int userId) {
Kweku Adams35d05ac2020-02-25 16:44:06 -08003049 mQuotaController.clearAppStats(userId, pkgName);
Kweku Adams87a4cd52020-02-24 17:20:04 -08003050 }
3051
Kweku Adams9239a1a2019-11-12 12:06:37 -08003052 void resetScheduleQuota() {
3053 mQuotaTracker.clear();
3054 }
3055
lpeter318abc92018-05-04 16:13:14 +08003056 void triggerDockState(boolean idleState) {
3057 final Intent dockIntent;
3058 if (idleState) {
3059 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3060 } else {
3061 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3062 }
3063 dockIntent.setPackage("android");
3064 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3065 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3066 }
3067
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003068 static void dumpHelp(PrintWriter pw) {
3069 pw.println("Job Scheduler (jobscheduler) dump options:");
3070 pw.println(" [-h] [package] ...");
3071 pw.println(" -h: print this help");
3072 pw.println(" [package] is an optional package name to limit the output to.");
3073 }
3074
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003075 /** Sort jobs by caller UID, then by Job ID. */
3076 private static void sortJobs(List<JobStatus> jobs) {
3077 Collections.sort(jobs, new Comparator<JobStatus>() {
3078 @Override
3079 public int compare(JobStatus o1, JobStatus o2) {
3080 int uid1 = o1.getUid();
3081 int uid2 = o2.getUid();
3082 int id1 = o1.getJobId();
3083 int id2 = o2.getJobId();
3084 if (uid1 != uid2) {
3085 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003086 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003087 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003088 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003089 });
3090 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003091
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003092 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003093 final int filterUidFinal = UserHandle.getAppId(filterUid);
Makoto Onukif731c422019-01-22 13:23:31 -08003094 final long now = sSystemClock.millis();
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003095 final long nowElapsed = sElapsedRealtimeClock.millis();
3096 final long nowUptime = sUptimeMillisClock.millis();
Makoto Onukif731c422019-01-22 13:23:31 -08003097
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003098 final Predicate<JobStatus> predicate = (js) -> {
3099 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3100 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3101 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003102 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003103 mConstants.dump(pw);
Kweku Adams4e774a32019-04-24 18:01:29 -07003104 for (StateController controller : mControllers) {
3105 pw.increaseIndent();
3106 controller.dumpConstants(pw);
3107 pw.decreaseIndent();
3108 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003109 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003110
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003111 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3112 pw.print(" ");
3113 mJobRestrictions.get(i).dumpConstants(pw);
3114 pw.println();
3115 }
Makoto Onukib5d5e972018-02-20 14:44:20 -08003116 pw.println();
3117
Kweku Adams9239a1a2019-11-12 12:06:37 -08003118 mQuotaTracker.dump(pw);
3119 pw.println();
3120
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003121 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003122 pw.print("Registered ");
3123 pw.print(mJobs.size());
3124 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003125 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003126 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003127 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003128 for (JobStatus job : jobs) {
3129 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3130 pw.println(job.toShortStringExceptUniqueId());
3131
3132 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003133 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003134 continue;
3135 }
3136
Dianne Hackborn6d068262017-05-16 13:14:37 -07003137 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003138
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003139
3140 pw.print(" Restricted due to:");
3141 final boolean isRestricted = checkIfRestricted(job) != null;
3142 if (isRestricted) {
3143 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3144 final JobRestriction restriction = mJobRestrictions.get(i);
3145 if (restriction.isJobRestricted(job)) {
3146 final int reason = restriction.getReason();
Kweku Adams13289702020-02-19 17:28:55 -08003147 pw.print(" " + JobParameters.getReasonCodeDescription(reason));
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003148 }
3149 }
3150 } else {
3151 pw.print(" none");
3152 }
3153 pw.println(".");
3154
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003155 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003156 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003157 pw.print(" (job=");
3158 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003159 pw.print(" user=");
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003160 pw.print(areUsersStartedLocked(job));
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003161 pw.print(" !restricted=");
3162 pw.print(!isRestricted);
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003163 pw.print(" !pending=");
3164 pw.print(!mPendingJobs.contains(job));
3165 pw.print(" !active=");
3166 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003167 pw.print(" !backingup=");
3168 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003169 pw.print(" comp=");
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003170 pw.print(isComponentUsable(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003171 pw.println(")");
3172 }
Christopher Tate7060b042014-06-09 19:50:00 -07003173 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003174 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003175 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003176 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003177 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003178 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3179 pw.increaseIndent();
3180 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3181 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003182 }
3183 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003184 pw.println("Uid priority overrides:");
3185 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003186 int uid = mUidPriorityOverride.keyAt(i);
3187 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3188 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3189 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3190 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003191 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003192 if (mBackingUpUids.size() > 0) {
3193 pw.println();
3194 pw.println("Backing up uids:");
3195 boolean first = true;
3196 for (int i = 0; i < mBackingUpUids.size(); i++) {
3197 int uid = mBackingUpUids.keyAt(i);
3198 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3199 if (first) {
3200 pw.print(" ");
3201 first = false;
3202 } else {
3203 pw.print(", ");
3204 }
3205 pw.print(UserHandle.formatUid(uid));
3206 }
3207 }
3208 pw.println();
3209 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003210 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003211 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003212 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003213 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3214 pw.println();
3215 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003216 pw.println("Pending queue:");
3217 for (int i=0; i<mPendingJobs.size(); i++) {
3218 JobStatus job = mPendingJobs.get(i);
3219 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3220 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003221 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003222 int priority = evaluateJobPriorityLocked(job);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003223 pw.print(" Evaluated priority: ");
3224 pw.println(JobInfo.getPriorityString(priority));
3225
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003226 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003227 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003228 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003229 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003230 }
Christopher Tate7060b042014-06-09 19:50:00 -07003231 pw.println();
3232 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003233 for (int i=0; i<mActiveServices.size(); i++) {
3234 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003235 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003236 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003237 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003238 if (jsc.mStoppedReason != null) {
3239 pw.print("inactive since ");
3240 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3241 pw.print(", stopped because: ");
3242 pw.println(jsc.mStoppedReason);
3243 } else {
3244 pw.println("inactive");
3245 }
Christopher Tate7060b042014-06-09 19:50:00 -07003246 continue;
3247 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003248 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003249 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003250 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003251 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003252 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003253 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003254 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003255 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003256 pw.print(" Evaluated priority: ");
3257 pw.println(JobInfo.getPriorityString(priority));
3258
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003259 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003260 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003261 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003262 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3263 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003264 }
3265 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003266 if (filterUid == -1) {
3267 pw.println();
3268 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3269 pw.print("mReportedActive="); pw.println(mReportedActive);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003270 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003271 pw.println();
Makoto Onukic279f2b2019-01-09 13:11:37 -08003272
Makoto Onukif731c422019-01-22 13:23:31 -08003273 mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
Makoto Onukic279f2b2019-01-09 13:11:37 -08003274
3275 pw.println();
Makoto Onukie7b02982017-08-24 14:23:36 -07003276 pw.print("PersistStats: ");
3277 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003278 }
3279 pw.println();
3280 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003281
3282 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3283 ProtoOutputStream proto = new ProtoOutputStream(fd);
3284 final int filterUidFinal = UserHandle.getAppId(filterUid);
Makoto Onukif731c422019-01-22 13:23:31 -08003285 final long now = sSystemClock.millis();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003286 final long nowElapsed = sElapsedRealtimeClock.millis();
3287 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003288 final Predicate<JobStatus> predicate = (js) -> {
3289 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3290 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3291 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003292
3293 synchronized (mLock) {
Kweku Adams4e774a32019-04-24 18:01:29 -07003294 final long settingsToken = proto.start(JobSchedulerServiceDumpProto.SETTINGS);
3295 mConstants.dump(proto);
3296 for (StateController controller : mControllers) {
3297 controller.dumpConstants(proto);
3298 }
3299 proto.end(settingsToken);
3300
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003301 for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
3302 mJobRestrictions.get(i).dumpConstants(proto);
3303 }
Makoto Onukib5d5e972018-02-20 14:44:20 -08003304
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003305 for (int u : mStartedUsers) {
3306 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3307 }
Kweku Adams9239a1a2019-11-12 12:06:37 -08003308
3309 mQuotaTracker.dump(proto, JobSchedulerServiceDumpProto.QUOTA_TRACKER);
3310
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003311 if (mJobs.size() > 0) {
3312 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3313 sortJobs(jobs);
3314 for (JobStatus job : jobs) {
3315 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3316 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3317
3318 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003319 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003320 continue;
3321 }
3322
3323 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3324
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003325 proto.write(
3326 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY_TO_BE_EXECUTED,
3327 isReadyToBeExecutedLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003328 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3329 job.isReady());
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003330 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003331 areUsersStartedLocked(job));
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003332 proto.write(
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003333 JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
3334 checkIfRestricted(job) != null);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003335 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3336 mPendingJobs.contains(job));
3337 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3338 isCurrentlyActiveLocked(job));
3339 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3340 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003341 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
3342 isComponentUsable(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003343
Sergey Nikolaienkov2ad8af12019-08-26 13:13:08 +02003344 for (JobRestriction restriction : mJobRestrictions) {
3345 final long restrictionsToken = proto.start(
3346 JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
3347 proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
3348 restriction.getReason());
3349 proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
3350 restriction.isJobRestricted(job));
3351 proto.end(restrictionsToken);
3352 }
3353
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003354 proto.end(rjToken);
3355 }
3356 }
3357 for (StateController controller : mControllers) {
3358 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003359 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003360 }
3361 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3362 int uid = mUidPriorityOverride.keyAt(i);
3363 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3364 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3365 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3366 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3367 mUidPriorityOverride.valueAt(i));
3368 proto.end(pToken);
3369 }
3370 }
3371 for (int i = 0; i < mBackingUpUids.size(); i++) {
3372 int uid = mBackingUpUids.keyAt(i);
3373 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3374 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3375 }
3376 }
3377
3378 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3379 filterUidFinal);
3380 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3381 filterUidFinal);
3382
3383 for (JobStatus job : mPendingJobs) {
3384 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3385
3386 job.writeToShortProto(proto, PendingJob.INFO);
3387 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003388 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003389 proto.write(PendingJob.PENDING_DURATION_MS, nowUptime - job.madePending);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003390
3391 proto.end(pjToken);
3392 }
3393 for (JobServiceContext jsc : mActiveServices) {
3394 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3395 final JobStatus job = jsc.getRunningJobLocked();
3396
3397 if (job == null) {
3398 final long ijToken = proto.start(ActiveJob.INACTIVE);
3399
3400 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3401 nowElapsed - jsc.mStoppedTime);
3402 if (jsc.mStoppedReason != null) {
3403 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3404 jsc.mStoppedReason);
3405 }
3406
3407 proto.end(ijToken);
3408 } else {
3409 final long rjToken = proto.start(ActiveJob.RUNNING);
3410
3411 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3412
3413 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3414 nowElapsed - jsc.getExecutionStartTimeElapsed());
3415 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3416 jsc.getTimeoutElapsed() - nowElapsed);
3417
3418 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3419
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003420 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
3421 evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003422
3423 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3424 nowUptime - job.madeActive);
3425 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3426 job.madeActive - job.madePending);
3427
3428 proto.end(rjToken);
3429 }
3430 proto.end(ajToken);
3431 }
3432 if (filterUid == -1) {
3433 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3434 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003435 }
Makoto Onukif731c422019-01-22 13:23:31 -08003436 mConcurrencyManager.dumpProtoLocked(proto,
3437 JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
Kweku Adamsc3f9ba62019-08-01 12:14:55 -07003438
Jeffrey Huangcb782852019-12-05 11:28:11 -08003439 mJobs.getPersistStats().dumpDebug(proto, JobSchedulerServiceDumpProto.PERSIST_STATS);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003440 }
3441
3442 proto.flush();
3443 }
Christopher Tate7060b042014-06-09 19:50:00 -07003444}