blob: 7625aafd090702f04a86d65914be2a008fa6f4c1 [file] [log] [blame]
Christopher Tate7060b042014-06-09 19:50:00 -07001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.job;
18
Christopher Tateb5c07882016-05-26 17:11:09 -070019import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
20import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
21
Christopher Tate435c2f42018-01-18 12:59:15 -080022import android.annotation.UserIdInt;
Christopher Tateee7805b2016-07-15 16:56:56 -070023import android.app.Activity;
Dianne Hackborn8ad2af72015-03-17 17:00:24 -070024import android.app.ActivityManager;
Makoto Onuki15407842018-01-19 14:23:11 -080025import android.app.ActivityManagerInternal;
Christopher Tated1aebb32018-01-31 13:24:14 -080026import android.app.AlarmManager;
Christopher Tate5568f542014-06-18 13:53:31 -070027import android.app.AppGlobals;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -070028import android.app.IUidObserver;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070029import android.app.job.IJobScheduler;
Christopher Tate7060b042014-06-09 19:50:00 -070030import android.app.job.JobInfo;
Shreyas Basarge5db09082016-01-07 13:38:29 +000031import android.app.job.JobParameters;
Tej Singh33a412b2018-03-16 18:43:59 -070032import android.app.job.JobProtoEnums;
Christopher Tate7060b042014-06-09 19:50:00 -070033import android.app.job.JobScheduler;
34import android.app.job.JobService;
Serik Beketayev75915d12018-08-01 16:56:59 -070035import android.app.job.JobSnapshot;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070036import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080037import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070038import android.app.usage.UsageStatsManagerInternal;
39import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070040import android.content.BroadcastReceiver;
41import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070042import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070043import android.content.Context;
44import android.content.Intent;
45import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070046import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070047import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060048import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070049import android.content.pm.PackageManagerInternal;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070050import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070051import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070052import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070053import android.os.BatteryStats;
Amith Yamasani977e11f2018-02-16 11:29:54 -080054import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070055import android.os.Binder;
56import android.os.Handler;
Wei Wang8c0c3c12018-11-14 14:56:52 -080057import android.os.IThermalService;
58import android.os.IThermalStatusListener;
Christopher Tate7060b042014-06-09 19:50:00 -070059import android.os.Looper;
60import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070061import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070062import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080063import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070064import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070065import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070066import android.os.SystemClock;
Wei Wang8c0c3c12018-11-14 14:56:52 -080067import android.os.Temperature;
Christopher Tate7060b042014-06-09 19:50:00 -070068import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070069import android.os.UserManagerInternal;
Kweku Adamsbffea5a2018-12-13 22:13:28 -080070import android.os.WorkSource;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070071import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080072import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070073import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070074import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070075import android.util.Slog;
76import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080077import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080078import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080079import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080080import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080081
Wei Wang8c0c3c12018-11-14 14:56:52 -080082import com.android.internal.annotations.GuardedBy;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070083import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070084import com.android.internal.app.IBatteryStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070085import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060086import com.android.internal.util.DumpUtils;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070087import com.android.internal.util.IndentingPrintWriter;
Makoto Onuki15407842018-01-19 14:23:11 -080088import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070089import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080090import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070091import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080092import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080093import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
94import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Makoto Onukib5d5e972018-02-20 14:44:20 -080095import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070096import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070097import com.android.server.job.controllers.BatteryController;
98import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080099import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -0700100import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -0700101import com.android.server.job.controllers.IdleController;
102import com.android.server.job.controllers.JobStatus;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800103import com.android.server.job.controllers.QuotaController;
Christopher Tate7060b042014-06-09 19:50:00 -0700104import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700105import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700106import com.android.server.job.controllers.TimeController;
107
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700108import libcore.util.EmptyArray;
109
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700110import java.io.FileDescriptor;
111import java.io.PrintWriter;
112import java.time.Clock;
113import java.util.ArrayList;
114import java.util.Arrays;
115import java.util.Collections;
116import java.util.Comparator;
Christopher Tate325768c2018-03-07 16:07:56 -0800117import java.util.HashMap;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700118import java.util.List;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700119import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800120import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700121
Christopher Tate7060b042014-06-09 19:50:00 -0700122/**
123 * Responsible for taking jobs representing work to be performed by a client app, and determining
124 * based on the criteria specified when that job should be run against the client application's
125 * endpoint.
126 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
127 * about constraints, or the state of active jobs. It receives callbacks from the various
128 * controllers and completed jobs and operates accordingly.
129 *
130 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
131 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
132 * @hide
133 */
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600134public class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700135 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700136 public static final String TAG = "JobScheduler";
137 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700138 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800139
Dianne Hackborn970510b2016-02-24 16:56:42 -0800140 /** The maximum number of concurrent jobs we run at one time. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800141 static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800142 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800143 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800144 /** The maximum number of jobs that we allow an unprivileged app to schedule */
145 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700146
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700147 @VisibleForTesting
148 public static Clock sSystemClock = Clock.systemUTC();
149 @VisibleForTesting
150 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
151 @VisibleForTesting
152 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800153
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800154 /** Global local for all job scheduler state. */
155 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700156 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700157 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700158 /** Tracking the standby bucket state of each app */
159 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700160 /** Tracking amount of time each package runs for. */
161 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Makoto Onuki714f97d2018-12-05 11:18:13 -0800162 final JobConcurrencyManager mConcurrencyManager;
Christopher Tate7060b042014-06-09 19:50:00 -0700163
164 static final int MSG_JOB_EXPIRED = 0;
165 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700166 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000167 static final int MSG_CHECK_JOB_GREEDY = 3;
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800168 static final int MSG_UID_STATE_CHANGED = 4;
169 static final int MSG_UID_GONE = 5;
170 static final int MSG_UID_ACTIVE = 6;
171 static final int MSG_UID_IDLE = 7;
Christopher Tate7060b042014-06-09 19:50:00 -0700172
Christopher Tate7060b042014-06-09 19:50:00 -0700173 /**
174 * Track Services that have currently active or pending jobs. The index is provided by
175 * {@link JobStatus#getServiceToken()}
176 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700177 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700178
Christopher Tate7060b042014-06-09 19:50:00 -0700179 /** List of controllers that will notify this service of updates to jobs. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800180 final List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800181 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700182 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700183 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700184 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700185 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700186 private final DeviceIdleJobsController mDeviceIdleJobsController;
Wei Wang8c0c3c12018-11-14 14:56:52 -0800187 /** Need directly for receiving thermal events */
188 private IThermalService mThermalService;
189 /** Thermal constraint. */
190 @GuardedBy("mLock")
191 private boolean mThermalConstraint = false;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700192
Christopher Tate7060b042014-06-09 19:50:00 -0700193 /**
194 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
195 * when ready to execute them.
196 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700197 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700198
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700199 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700200
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700201 final JobHandler mHandler;
202 final JobSchedulerStub mJobSchedulerStub;
203
Christopher Tatea732f012017-10-26 17:26:53 -0700204 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800205 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700206 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800207 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800208 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800209 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700210
211 /**
212 * Set to true once we are allowed to run third party apps.
213 */
214 boolean mReadyToRock;
215
Christopher Tate7060b042014-06-09 19:50:00 -0700216 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800217 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800218 */
219 boolean mReportedActive;
220
221 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800222 * Are we currently in device-wide standby parole?
223 */
224 volatile boolean mInParole;
225
226 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800227 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800228 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800229 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
230
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700231 /**
232 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
233 */
234 final SparseIntArray mBackingUpUids = new SparseIntArray();
235
Christopher Tatea732f012017-10-26 17:26:53 -0700236 /**
237 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
238 * next become runnable. Index into this array is by normalized bucket:
239 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
240 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
241 * and NEVER apps don't get them at all.
242 */
243 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
244 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800245 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
246
Christopher Tate325768c2018-03-07 16:07:56 -0800247 /**
248 * Named indices into the STANDBY_BEATS array, for clarity in referring to
249 * specific buckets' bookkeeping.
250 */
Kweku Adams4836f9d2018-11-12 17:04:17 -0800251 public static final int ACTIVE_INDEX = 0;
252 public static final int WORKING_INDEX = 1;
253 public static final int FREQUENT_INDEX = 2;
254 public static final int RARE_INDEX = 3;
255 public static final int NEVER_INDEX = 4;
Christopher Tate325768c2018-03-07 16:07:56 -0800256
257 /**
258 * Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
259 * rather than rely on Usage Stats' timestamps, because heartbeat time can be
260 * manipulated for testing purposes and we need job runnability to track that rather
261 * than real time.
262 *
263 * Outer SparseArray slices by user handle; inner map of package name to heartbeat
264 * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
265 * and it will be accessed in a known-hot code path.
266 */
267 final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
268
Christopher Tated1aebb32018-01-31 13:24:14 -0800269 static final String HEARTBEAT_TAG = "*job.heartbeat*";
270 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700271
Dianne Hackborn970510b2016-02-24 16:56:42 -0800272 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
273
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700274 private class ConstantsObserver extends ContentObserver {
275 private ContentResolver mResolver;
276
277 public ConstantsObserver(Handler handler) {
278 super(handler);
279 }
280
281 public void start(ContentResolver resolver) {
282 mResolver = resolver;
283 mResolver.registerContentObserver(Settings.Global.getUriFor(
284 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
285 updateConstants();
286 }
287
288 @Override
289 public void onChange(boolean selfChange, Uri uri) {
290 updateConstants();
291 }
292
293 private void updateConstants() {
294 synchronized (mLock) {
295 try {
296 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
297 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Kweku Adams4836f9d2018-11-12 17:04:17 -0800298 for (int controller = 0; controller < mControllers.size(); controller++) {
299 final StateController sc = mControllers.get(controller);
300 sc.onConstantsUpdatedLocked();
301 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700302 } catch (IllegalArgumentException e) {
303 // Failed to parse the settings string, log this and move on
304 // with defaults.
305 Slog.e(TAG, "Bad jobscheduler settings", e);
306 }
307 }
308
Kweku Adams4836f9d2018-11-12 17:04:17 -0800309 if (mConstants.USE_HEARTBEATS) {
310 // Reset the heartbeat alarm based on the new heartbeat duration
311 setNextHeartbeatAlarm();
312 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700313 }
314 }
315
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800316 /**
Wei Wang8c0c3c12018-11-14 14:56:52 -0800317 * Thermal event received from Thermal Service
318 */
319 private final class ThermalStatusListener extends IThermalStatusListener.Stub {
320 @Override public void onStatusChange(int status) {
321 // Throttle for Temperature.THROTTLING_SEVERE and above
322 synchronized (mLock) {
323 mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
324 }
325 onControllerStateChanged();
326 }
327 }
328
Makoto Onukic279f2b2019-01-09 13:11:37 -0800329 static class MaxJobCounts {
Makoto Onuki66a51442018-12-20 14:23:50 -0800330 private final KeyValueListParser.IntValue mTotal;
Makoto Onukic279f2b2019-01-09 13:11:37 -0800331 private final KeyValueListParser.IntValue mMaxBg;
332 private final KeyValueListParser.IntValue mMinBg;
Makoto Onuki66a51442018-12-20 14:23:50 -0800333
Makoto Onukic279f2b2019-01-09 13:11:37 -0800334 MaxJobCounts(int totalDefault, String totalKey,
335 int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
Makoto Onuki66a51442018-12-20 14:23:50 -0800336 mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800337 mMaxBg = new KeyValueListParser.IntValue(maxBgKey, maxBgDefault);
338 mMinBg = new KeyValueListParser.IntValue(minBgKey, minBgDefault);
Makoto Onuki66a51442018-12-20 14:23:50 -0800339 }
340
341 public void parse(KeyValueListParser parser) {
342 mTotal.parse(parser);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800343 mMaxBg.parse(parser);
344 mMinBg.parse(parser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800345
Makoto Onukic279f2b2019-01-09 13:11:37 -0800346 if (mTotal.getValue() < 1) {
347 mTotal.setValue(1);
348 } else if (mTotal.getValue() > MAX_JOB_CONTEXTS_COUNT) {
349 mTotal.setValue(MAX_JOB_CONTEXTS_COUNT);
Makoto Onuki66a51442018-12-20 14:23:50 -0800350 }
351
Makoto Onukic279f2b2019-01-09 13:11:37 -0800352 if (mMaxBg.getValue() < 1) {
353 mMaxBg.setValue(1);
354 } else if (mMaxBg.getValue() > mTotal.getValue()) {
355 mMaxBg.setValue(mTotal.getValue());
356 }
357 if (mMinBg.getValue() < 0) {
358 mMinBg.setValue(0);
359 } else {
360 if (mMinBg.getValue() > mMaxBg.getValue()) {
361 mMinBg.setValue(mMaxBg.getValue());
362 }
363 if (mMinBg.getValue() >= mTotal.getValue()) {
364 mMinBg.setValue(mTotal.getValue() - 1);
365 }
366 }
Makoto Onuki66a51442018-12-20 14:23:50 -0800367 }
368
Makoto Onukif731c422019-01-22 13:23:31 -0800369 /** Total number of jobs to run simultaneously. */
370 public int getMaxTotal() {
Makoto Onuki66a51442018-12-20 14:23:50 -0800371 return mTotal.getValue();
372 }
373
Makoto Onukif731c422019-01-22 13:23:31 -0800374 /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
Makoto Onukic279f2b2019-01-09 13:11:37 -0800375 public int getMaxBg() {
376 return mMaxBg.getValue();
377 }
378
Makoto Onukif731c422019-01-22 13:23:31 -0800379 /**
380 * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
381 * pending, rather than always running the TOTAL number of FG jobs.
382 */
Makoto Onukic279f2b2019-01-09 13:11:37 -0800383 public int getMinBg() {
384 return mMinBg.getValue();
Makoto Onuki66a51442018-12-20 14:23:50 -0800385 }
386
387 public void dump(PrintWriter pw, String prefix) {
388 mTotal.dump(pw, prefix);
Makoto Onukic279f2b2019-01-09 13:11:37 -0800389 mMaxBg.dump(pw, prefix);
390 mMinBg.dump(pw, prefix);
Makoto Onuki66a51442018-12-20 14:23:50 -0800391 }
392
Makoto Onukif731c422019-01-22 13:23:31 -0800393 public void dumpProto(ProtoOutputStream proto, long fieldId) {
394 final long token = proto.start(fieldId);
395 mTotal.dumpProto(proto, MaxJobCountsProto.TOTAL_JOBS);
396 mMaxBg.dumpProto(proto, MaxJobCountsProto.MAX_BG);
397 mMinBg.dumpProto(proto, MaxJobCountsProto.MIN_BG);
398 proto.end(token);
399 }
400 }
401
402 /** {@link MaxJobCounts} for each memory trim level. */
403 static class MaxJobCountsPerMemoryTrimLevel {
404 public final MaxJobCounts normal;
405 public final MaxJobCounts moderate;
406 public final MaxJobCounts low;
407 public final MaxJobCounts critical;
408
409 MaxJobCountsPerMemoryTrimLevel(
410 MaxJobCounts normal,
411 MaxJobCounts moderate, MaxJobCounts low,
412 MaxJobCounts critical) {
413 this.normal = normal;
414 this.moderate = moderate;
415 this.low = low;
416 this.critical = critical;
417 }
418
419 public void dumpProto(ProtoOutputStream proto, long fieldId) {
420 final long token = proto.start(fieldId);
421 normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL);
422 moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE);
423 low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW);
424 critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL);
425 proto.end(token);
Makoto Onuki66a51442018-12-20 14:23:50 -0800426 }
427 }
428
Wei Wang8c0c3c12018-11-14 14:56:52 -0800429 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700430 * All times are in milliseconds. These constants are kept synchronized with the system
431 * global Settings. Any access to this class or its fields should be done while
432 * holding the JobSchedulerService.mLock lock.
433 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700434 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700435 // Key names stored in the settings value.
436 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
437 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800438 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700439 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700440 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
441 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
442 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
443 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
444 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
Makoto Onukic279f2b2019-01-09 13:11:37 -0800445
446 // The following values used to be used on P and below. Do not reuse them.
447 private static final String DEPRECATED_KEY_FG_JOB_COUNT = "fg_job_count";
448 private static final String DEPRECATED_KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
449 private static final String DEPRECATED_KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
450 private static final String DEPRECATED_KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
451 private static final String DEPRECATED_KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
452
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700453 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
454 = "max_standard_reschedule_count";
455 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
456 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
457 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700458 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
459 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
460 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
461 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700462 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
463 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Kweku Adams4836f9d2018-11-12 17:04:17 -0800464 private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800465 private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
466 "tc_skip_not_ready_jobs";
Kweku Adams4836f9d2018-11-12 17:04:17 -0800467 private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
468 "qc_allowed_time_per_period_ms";
469 private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
470 "qc_in_quota_buffer_ms";
471 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
472 "qc_window_size_active_ms";
473 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
474 "qc_window_size_working_ms";
475 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
476 "qc_window_size_frequent_ms";
477 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
478 "qc_window_size_rare_ms";
Kweku Adams045fb5722018-12-11 14:29:10 -0800479 private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
480 "qc_max_execution_time_ms";
Kweku Adams288e73b2019-01-17 13:53:24 -0800481 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
482 "qc_max_job_count_active";
483 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
484 "qc_max_job_count_working";
485 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
486 "qc_max_job_count_frequent";
487 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
488 "qc_max_job_count_rare";
489 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
490 "qc_max_count_per_allowed_time";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700491
492 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
493 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800494 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700495 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700496 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
497 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
498 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
499 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
500 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700501 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
502 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
503 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
504 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700505 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000506 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
507 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700508 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700509 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
510 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800511 private static final boolean DEFAULT_USE_HEARTBEATS = true;
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800512 private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800513 private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
514 10 * 60 * 1000L; // 10 minutes
515 private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
516 30 * 1000L; // 30 seconds
517 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
518 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
519 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
520 2 * 60 * 60 * 1000L; // 2 hours
521 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
522 8 * 60 * 60 * 1000L; // 8 hours
523 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
524 24 * 60 * 60 * 1000L; // 24 hours
Kweku Adams045fb5722018-12-11 14:29:10 -0800525 private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
526 4 * 60 * 60 * 1000L; // 4 hours
Kweku Adams288e73b2019-01-17 13:53:24 -0800527 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
528 200; // 1200/hr
529 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
530 1200; // 600/hr
531 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
532 1800; // 225/hr
533 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
534 2400; // 100/hr
535 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700536
537 /**
538 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
539 * early.
540 */
541 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
542 /**
543 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
544 * things early.
545 */
546 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
547 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800548 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
549 * schedule things early.
550 */
551 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
552 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700553 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
554 * schedule things early.
555 */
556 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
557 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700558 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
559 * things early. 1 == Run connectivity jobs as soon as ready.
560 */
561 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
562 /**
563 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
564 * schedule things early.
565 */
566 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
567 /**
568 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
569 * running some work early. This (and thus the other min counts) is now set to 1, to
570 * prevent any batching at this level. Since we now do batching through doze, that is
571 * a much better mechanism.
572 */
573 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
574 /**
575 * This is the job execution factor that is considered to be heavy use of the system.
576 */
577 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
578 /**
579 * This is the job execution factor that is considered to be moderate use of the system.
580 */
581 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
Makoto Onuki66a51442018-12-20 14:23:50 -0800582
583 // Max job counts for screen on / off, for each memory trim level.
Makoto Onukif731c422019-01-22 13:23:31 -0800584 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
585 new MaxJobCountsPerMemoryTrimLevel(
586 new MaxJobCounts(
587 8, "max_job_total_on_normal",
588 6, "max_job_max_bg_on_normal",
589 2, "max_job_min_bg_on_normal"),
590 new MaxJobCounts(
591 8, "max_job_total_on_moderate",
592 4, "max_job_max_bg_on_moderate",
593 2, "max_job_min_bg_on_moderate"),
594 new MaxJobCounts(
595 5, "max_job_total_on_low",
596 1, "max_job_max_bg_on_low",
597 1, "max_job_min_bg_on_low"),
598 new MaxJobCounts(
599 5, "max_job_total_on_critical",
600 1, "max_job_max_bg_on_critical",
601 1, "max_job_min_bg_on_critical"));
Makoto Onuki66a51442018-12-20 14:23:50 -0800602
Makoto Onukif731c422019-01-22 13:23:31 -0800603 final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
604 new MaxJobCountsPerMemoryTrimLevel(
605 new MaxJobCounts(
606 10, "max_job_total_off_normal",
607 6, "max_job_max_bg_off_normal",
608 2, "max_job_min_bg_off_normal"),
609 new MaxJobCounts(
610 10, "max_job_total_off_moderate",
611 4, "max_job_max_bg_off_moderate",
612 2, "max_job_min_bg_off_moderate"),
613 new MaxJobCounts(
614 5, "max_job_total_off_low",
615 1, "max_job_max_bg_off_low",
616 1, "max_job_min_bg_off_low"),
617 new MaxJobCounts(
618 5, "max_job_total_off_critical",
619 1, "max_job_max_bg_off_critical",
620 1, "max_job_min_bg_off_critical"));
Makoto Onuki66a51442018-12-20 14:23:50 -0800621
Makoto Onukic279f2b2019-01-09 13:11:37 -0800622
623 /** Wait for this long after screen off before increasing the job concurrency. */
624 final KeyValueListParser.IntValue SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
625 new KeyValueListParser.IntValue(
626 "screen_off_job_concurrency_increase_delay_ms", 30_000);
Makoto Onuki66a51442018-12-20 14:23:50 -0800627
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700628 /**
629 * The maximum number of times we allow a job to have itself rescheduled before
630 * giving up on it, for standard jobs.
631 */
632 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
633 /**
634 * The maximum number of times we allow a job to have itself rescheduled before
635 * giving up on it, for jobs that are executing work.
636 */
637 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
638 /**
639 * The minimum backoff time to allow for linear backoff.
640 */
641 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
642 /**
643 * The minimum backoff time to allow for exponential backoff.
644 */
645 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700646 /**
647 * How often we recalculate runnability based on apps' standby bucket assignment.
648 * This should be prime relative to common time interval lengths such as a quarter-
649 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
650 */
651 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700652 /**
653 * Mapping: standby bucket -> number of heartbeats between each sweep of that
654 * bucket's jobs.
655 *
656 * Bucket assignments as recorded in the JobStatus objects are normalized to be
657 * indices into this array, rather than the raw constants used
658 * by AppIdleHistory.
659 */
660 final int[] STANDBY_BEATS = {
661 0,
662 DEFAULT_STANDBY_WORKING_BEATS,
663 DEFAULT_STANDBY_FREQUENT_BEATS,
664 DEFAULT_STANDBY_RARE_BEATS
665 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700666 /**
667 * The fraction of a job's running window that must pass before we
668 * consider running it when the network is congested.
669 */
670 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
671 /**
672 * The fraction of a prefetch job's running window that must pass before
673 * we consider matching it against a metered network.
674 */
675 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800676 /**
677 * Whether to use heartbeats or rolling window for quota management. True will use
678 * heartbeats, false will use a rolling window.
679 */
680 public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
681
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800682 /**
683 * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
684 * ready now.
685 */
686 public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
687 DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS;
688
Kweku Adams4836f9d2018-11-12 17:04:17 -0800689 /** How much time each app will have to run jobs within their standby bucket window. */
690 public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
691 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
692
693 /**
694 * How much time the package should have before transitioning from out-of-quota to in-quota.
695 * This should not affect processing if the package is already in-quota.
696 */
697 public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
698 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
699
700 /**
701 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800702 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800703 * WINDOW_SIZE_MS.
704 */
705 public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
706 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
707
708 /**
709 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800710 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800711 * WINDOW_SIZE_MS.
712 */
713 public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
714 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
715
716 /**
717 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800718 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800719 * WINDOW_SIZE_MS.
720 */
721 public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
722 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
723
724 /**
725 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800726 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800727 * WINDOW_SIZE_MS.
728 */
729 public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
730 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700731
Kweku Adams045fb5722018-12-11 14:29:10 -0800732 /**
733 * The maximum amount of time an app can have its jobs running within a 24 hour window.
734 */
735 public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
736 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
737
Kweku Adams288e73b2019-01-17 13:53:24 -0800738 /**
739 * The maximum number of jobs an app can run within this particular standby bucket's
740 * window size.
741 */
742 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
743 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE;
744
745 /**
746 * The maximum number of jobs an app can run within this particular standby bucket's
747 * window size.
748 */
749 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
750 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING;
751
752 /**
753 * The maximum number of jobs an app can run within this particular standby bucket's
754 * window size.
755 */
756 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
757 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT;
758
759 /**
760 * The maximum number of jobs an app can run within this particular standby bucket's
761 * window size.
762 */
763 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
764 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE;
765
766 /**
767 * The maximum number of jobs that can run within the past
768 * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
769 */
770 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
771 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
772
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700773 private final KeyValueListParser mParser = new KeyValueListParser(',');
774
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700775 void updateConstantsLocked(String value) {
776 try {
777 mParser.setString(value);
778 } catch (Exception e) {
779 // Failed to parse the settings string, log this and move on
780 // with defaults.
781 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700782 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800783
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700784 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
785 DEFAULT_MIN_IDLE_COUNT);
786 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
787 DEFAULT_MIN_CHARGING_COUNT);
788 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
789 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
790 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
791 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
792 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
793 DEFAULT_MIN_CONNECTIVITY_COUNT);
794 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
795 DEFAULT_MIN_CONTENT_COUNT);
796 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
797 DEFAULT_MIN_READY_JOBS_COUNT);
798 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
799 DEFAULT_HEAVY_USE_FACTOR);
800 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
801 DEFAULT_MODERATE_USE_FACTOR);
Makoto Onuki66a51442018-12-20 14:23:50 -0800802
Makoto Onukif731c422019-01-22 13:23:31 -0800803 MAX_JOB_COUNTS_SCREEN_ON.normal.parse(mParser);
804 MAX_JOB_COUNTS_SCREEN_ON.moderate.parse(mParser);
805 MAX_JOB_COUNTS_SCREEN_ON.low.parse(mParser);
806 MAX_JOB_COUNTS_SCREEN_ON.critical.parse(mParser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800807
Makoto Onukif731c422019-01-22 13:23:31 -0800808 MAX_JOB_COUNTS_SCREEN_OFF.normal.parse(mParser);
809 MAX_JOB_COUNTS_SCREEN_OFF.moderate.parse(mParser);
810 MAX_JOB_COUNTS_SCREEN_OFF.low.parse(mParser);
811 MAX_JOB_COUNTS_SCREEN_OFF.critical.parse(mParser);
Makoto Onuki66a51442018-12-20 14:23:50 -0800812
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700813 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
814 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
815 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
816 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
817 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
818 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
819 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
820 DEFAULT_MIN_EXP_BACKOFF_TIME);
821 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
822 DEFAULT_STANDBY_HEARTBEAT_TIME);
Christopher Tate325768c2018-03-07 16:07:56 -0800823 STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700824 DEFAULT_STANDBY_WORKING_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800825 STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700826 DEFAULT_STANDBY_FREQUENT_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800827 STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700828 DEFAULT_STANDBY_RARE_BEATS);
829 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
830 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
831 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
832 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800833 USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800834 TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean(
835 KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
836 DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800837 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
838 KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
839 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
840 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
841 KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
842 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
843 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
844 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
845 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
846 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
847 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
848 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
849 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
850 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
851 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
852 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
853 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
854 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800855 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
856 KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
857 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Kweku Adams288e73b2019-01-17 13:53:24 -0800858 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt(
859 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
860 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
861 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt(
862 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
863 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
864 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt(
865 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
866 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
867 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt(
868 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
869 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
870 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
871 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
872 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700873 }
874
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700875 void dump(IndentingPrintWriter pw) {
876 pw.println("Settings:");
877 pw.increaseIndent();
878 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
879 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
880 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
881 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
882 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
883 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
884 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
885 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
886 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
Makoto Onuki66a51442018-12-20 14:23:50 -0800887
Makoto Onukif731c422019-01-22 13:23:31 -0800888 MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, "");
889 MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, "");
890 MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, "");
891 MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, "");
Makoto Onuki66a51442018-12-20 14:23:50 -0800892
Makoto Onukif731c422019-01-22 13:23:31 -0800893 MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, "");
894 MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, "");
895 MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
896 MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
897
898 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dump(pw, "");
Makoto Onuki66a51442018-12-20 14:23:50 -0800899
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700900 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
901 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
902 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
903 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
904 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
905 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700906 pw.print(STANDBY_BEATS[0]);
907 for (int i = 1; i < STANDBY_BEATS.length; i++) {
908 pw.print(", ");
909 pw.print(STANDBY_BEATS[i]);
910 }
911 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700912 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
913 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
Kweku Adams4836f9d2018-11-12 17:04:17 -0800914 pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800915 pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
916 TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println();
Kweku Adams4836f9d2018-11-12 17:04:17 -0800917 pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
918 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
919 pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
920 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
921 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
922 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
923 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
924 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
925 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
926 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
927 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
928 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
Kweku Adams045fb5722018-12-11 14:29:10 -0800929 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
930 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
Kweku Adams288e73b2019-01-17 13:53:24 -0800931 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
932 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println();
933 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
934 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println();
935 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
936 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println();
937 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
938 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println();
939 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
940 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700941 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700942 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800943
944 void dump(ProtoOutputStream proto, long fieldId) {
945 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800946 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
947 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
948 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
949 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
950 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
951 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
952 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
953 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
954 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
Makoto Onuki66a51442018-12-20 14:23:50 -0800955
Makoto Onukif731c422019-01-22 13:23:31 -0800956 MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
957 MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
958
959 SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS.dumpProto(proto,
960 ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
Makoto Onuki66a51442018-12-20 14:23:50 -0800961
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800962 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
963 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
964 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
965 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
966 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800967 for (int period : STANDBY_BEATS) {
968 proto.write(ConstantsProto.STANDBY_BEATS, period);
969 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700970 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
971 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800972 proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
973
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800974 final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
975 proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS,
976 TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
977 proto.end(tcToken);
978
Kweku Adams4836f9d2018-11-12 17:04:17 -0800979 final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
980 proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
981 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
982 proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
983 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
984 proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
985 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
986 proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
987 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
988 proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
989 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
990 proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
991 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800992 proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
993 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Kweku Adams288e73b2019-01-17 13:53:24 -0800994 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE,
995 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
996 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
997 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
998 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
999 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
1000 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE,
1001 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
1002 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
1003 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
Kweku Adams4836f9d2018-11-12 17:04:17 -08001004 proto.end(qcToken);
1005
Kweku Adams85f2fbc2017-12-18 12:04:12 -08001006 proto.end(token);
1007 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001008 }
1009
1010 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001011 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001012
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001013 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
1014 if (o1.enqueueTime < o2.enqueueTime) {
1015 return -1;
1016 }
1017 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
1018 };
1019
1020 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
1021 int where = Collections.binarySearch(array, newItem, comparator);
1022 if (where < 0) {
1023 where = ~where;
1024 }
1025 array.add(where, newItem);
1026 }
1027
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001028 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001029 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
1030 * still clean up. On reinstall the package will have a new uid.
1031 */
1032 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1033 @Override
1034 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -07001035 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -07001036 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -07001037 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -07001038 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001039 final String pkgName = getPackageName(intent);
1040 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1041
Christopher Tateee7805b2016-07-15 16:56:56 -07001042 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -07001043 // Purge the app's jobs if the whole package was just disabled. When this is
1044 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -07001045 if (pkgName != null && pkgUid != -1) {
1046 final String[] changedComponents = intent.getStringArrayExtra(
1047 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1048 if (changedComponents != null) {
1049 for (String component : changedComponents) {
1050 if (component.equals(pkgName)) {
1051 if (DEBUG) {
1052 Slog.d(TAG, "Package state change: " + pkgName);
1053 }
1054 try {
1055 final int userId = UserHandle.getUserId(pkgUid);
1056 IPackageManager pm = AppGlobals.getPackageManager();
1057 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
1058 if (state == COMPONENT_ENABLED_STATE_DISABLED
1059 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
1060 if (DEBUG) {
1061 Slog.d(TAG, "Removing jobs for package " + pkgName
1062 + " in user " + userId);
1063 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001064 cancelJobsForPackageAndUid(pkgName, pkgUid,
1065 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -07001066 }
Christopher Tate652c5ad2016-10-05 14:45:46 -07001067 } catch (RemoteException|IllegalArgumentException e) {
1068 /*
1069 * IllegalArgumentException means that the package doesn't exist.
1070 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
1071 * behind outright uninstall, so by the time we try to act it's gone.
1072 * We don't need to act on this PACKAGE_CHANGED when this happens;
1073 * we'll get a PACKAGE_REMOVED later and clean up then.
1074 *
1075 * RemoteException can't actually happen; the package manager is
1076 * running in this same process.
1077 */
1078 }
Christopher Tateb5c07882016-05-26 17:11:09 -07001079 break;
1080 }
1081 }
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001082 if (DEBUG) {
1083 Slog.d(TAG, "Something in " + pkgName
1084 + " changed. Reevaluating controller states.");
1085 }
1086 synchronized (mLock) {
1087 for (int c = mControllers.size() - 1; c >= 0; --c) {
1088 mControllers.get(c).reevaluateStateLocked(pkgUid);
1089 }
1090 }
Christopher Tateb5c07882016-05-26 17:11:09 -07001091 }
1092 } else {
1093 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
1094 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001095 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -07001096 // If this is an outright uninstall rather than the first half of an
1097 // app update sequence, cancel the jobs associated with the app.
1098 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1099 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
1100 if (DEBUG) {
1101 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
1102 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001103 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -08001104 synchronized (mLock) {
1105 for (int c = 0; c < mControllers.size(); ++c) {
1106 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
1107 }
1108 }
Christopher Tate7060b042014-06-09 19:50:00 -07001109 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001110 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -07001111 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
1112 if (DEBUG) {
1113 Slog.d(TAG, "Removing jobs for user: " + userId);
1114 }
1115 cancelJobsForUser(userId);
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -08001116 synchronized (mLock) {
1117 for (int c = 0; c < mControllers.size(); ++c) {
1118 mControllers.get(c).onUserRemovedLocked(userId);
1119 }
1120 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001121 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
1122 // Has this package scheduled any jobs, such that we will take action
1123 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -07001124 if (pkgUid != -1) {
1125 List<JobStatus> jobsForUid;
1126 synchronized (mLock) {
1127 jobsForUid = mJobs.getJobsByUid(pkgUid);
1128 }
1129 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1130 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
1131 if (DEBUG) {
1132 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
1133 + pkgUid + " has jobs");
1134 }
1135 setResultCode(Activity.RESULT_OK);
1136 break;
1137 }
1138 }
1139 }
1140 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
1141 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -07001142 if (pkgUid != -1) {
1143 if (DEBUG) {
1144 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
1145 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001146 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -07001147 }
Christopher Tate7060b042014-06-09 19:50:00 -07001148 }
1149 }
1150 };
1151
Christopher Tateb5c07882016-05-26 17:11:09 -07001152 private String getPackageName(Intent intent) {
1153 Uri uri = intent.getData();
1154 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
1155 return pkg;
1156 }
1157
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001158 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001159 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001160 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001161 }
1162
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001163 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001164 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001165 }
1166
1167 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001168 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001169 }
1170
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001171 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001172 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001173 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001174
1175 @Override public void onUidCachedChanged(int uid, boolean cached) {
1176 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001177 };
1178
Jeff Sharkey4d89e422018-03-29 18:22:41 -06001179 public Context getTestableContext() {
1180 return getContext();
1181 }
1182
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001183 public Object getLock() {
1184 return mLock;
1185 }
1186
Dianne Hackborn8db0fc12016-04-12 13:48:25 -07001187 public JobStore getJobStore() {
1188 return mJobs;
1189 }
1190
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001191 public Constants getConstants() {
1192 return mConstants;
1193 }
1194
Kweku Adamsbffea5a2018-12-13 22:13:28 -08001195 public boolean isChainedAttributionEnabled() {
1196 return WorkSource.isChainedBatteryAttributionEnabled(getContext());
1197 }
1198
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001199 @Override
1200 public void onStartUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -08001201 synchronized (mLock) {
1202 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
1203 }
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001204 // Let's kick any outstanding jobs for this user.
1205 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1206 }
1207
1208 @Override
1209 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001210 // Let's kick any outstanding jobs for this user.
1211 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1212 }
1213
1214 @Override
1215 public void onStopUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -08001216 synchronized (mLock) {
1217 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
1218 }
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001219 }
1220
Makoto Onuki15407842018-01-19 14:23:11 -08001221 /**
Makoto Onukie4918212018-02-06 11:30:15 -08001222 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -08001223 */
Makoto Onukie4918212018-02-06 11:30:15 -08001224 private boolean isUidActive(int uid) {
1225 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -08001226 }
1227
Makoto Onukie4918212018-02-06 11:30:15 -08001228 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -08001229
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001230 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
1231 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001232 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001233 if (ActivityManager.getService().isAppStartModeDisabled(uId,
1234 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001235 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1236 + " -- package not allowed to start");
1237 return JobScheduler.RESULT_FAILURE;
1238 }
1239 } catch (RemoteException e) {
1240 }
Christopher Tatea732f012017-10-26 17:26:53 -07001241
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001242 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001243 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1244
1245 if (work != null && toCancel != null) {
1246 // Fast path: we are adding work to an existing job, and the JobInfo is not
1247 // changing. We can just directly enqueue this work in to the job.
1248 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -08001249
Dianne Hackborn342e6032017-04-13 18:04:31 -07001250 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -08001251
1252 // If any of work item is enqueued when the source is in the foreground,
1253 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -08001254 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001255
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001256 return JobScheduler.RESULT_SUCCESS;
1257 }
1258 }
1259
1260 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -08001261
1262 // Give exemption if the source is in the foreground just now.
1263 // Note if it's a sync job, this method is called on the handler so it's not exactly
1264 // the state when requestSync() was called, but that should be fine because of the
1265 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -08001266 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001267
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001268 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -08001269 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -08001270 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001271 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1272 Slog.w(TAG, "Too many jobs for uid " + uId);
1273 throw new IllegalStateException("Apps may not schedule more than "
1274 + MAX_JOBS_PER_APP + " distinct jobs");
1275 }
1276 }
1277
Dianne Hackborna47223f2017-03-30 13:49:13 -07001278 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001279 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001280
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001281 if (work != null) {
1282 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001283 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001284 }
Christopher Tate16032042018-10-05 15:53:50 -07001285
1286 if (toCancel != null) {
1287 // Implicitly replaces the existing job record with the new instance
1288 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
1289 } else {
1290 startTrackingJobLocked(jobStatus, null);
1291 }
Tej Singhd5747a62018-01-08 20:57:35 -08001292 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -08001293 uId, null, jobStatus.getBatteryName(),
Tej Singh33a412b2018-03-16 18:43:59 -07001294 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1295 JobProtoEnums.STOP_REASON_CANCELLED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001296
1297 // If the job is immediately ready to run, then we can just immediately
1298 // put it in the pending list and try to schedule it. This is especially
1299 // important for jobs with a 0 deadline constraint, since they will happen a fair
1300 // amount, we want to handle them as quickly as possible, and semantically we want to
1301 // make sure we have started holding the wake lock for the job before returning to
1302 // the caller.
1303 // If the job is not yet ready to run, there is nothing more to do -- we are
1304 // now just waiting for one of its controllers to change state and schedule
1305 // the job appropriately.
1306 if (isReadyToBeExecutedLocked(jobStatus)) {
1307 // This is a new job, we can just immediately put it on the pending
1308 // list and try to run it.
1309 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001310 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001311 maybeRunPendingJobsLocked();
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001312 } else {
1313 evaluateControllerStatesLocked(jobStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001314 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001315 }
Christopher Tate7060b042014-06-09 19:50:00 -07001316 return JobScheduler.RESULT_SUCCESS;
1317 }
1318
1319 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001320 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001321 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1322 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
1323 for (int i = jobs.size() - 1; i >= 0; i--) {
1324 JobStatus job = jobs.get(i);
1325 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -07001326 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001327 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -07001328 }
Christopher Tate7060b042014-06-09 19:50:00 -07001329 }
1330
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001331 public JobInfo getPendingJob(int uid, int jobId) {
1332 synchronized (mLock) {
1333 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1334 for (int i = jobs.size() - 1; i >= 0; i--) {
1335 JobStatus job = jobs.get(i);
1336 if (job.getJobId() == jobId) {
1337 return job.getJob();
1338 }
1339 }
1340 return null;
1341 }
1342 }
1343
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001344 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001345 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001346 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
1347 for (int i=0; i<jobsForUser.size(); i++) {
1348 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001349 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001350 }
Christopher Tate7060b042014-06-09 19:50:00 -07001351 }
1352 }
1353
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001354 private void cancelJobsForNonExistentUsers() {
1355 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
1356 synchronized (mLock) {
1357 mJobs.removeJobsOfNonUsers(umi.getUserIds());
1358 }
1359 }
1360
Makoto Onukie7b96182017-08-30 14:53:16 -07001361 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
1362 if ("android".equals(pkgName)) {
1363 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
1364 return;
1365 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001366 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001367 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1368 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1369 final JobStatus job = jobsForUid.get(i);
1370 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001371 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001372 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001373 }
1374 }
1375 }
1376
Christopher Tate7060b042014-06-09 19:50:00 -07001377 /**
1378 * Entry point from client to cancel all jobs originating from their uid.
1379 * This will remove the job from the master list, and cancel the job if it was staged for
1380 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -07001381 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -08001382 *
Christopher Tate7060b042014-06-09 19:50:00 -07001383 */
Christopher Tate8c67d122017-09-29 16:54:26 -07001384 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -07001385 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001386 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -07001387 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -07001388 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001389
1390 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001391 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001392 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1393 for (int i=0; i<jobsForUid.size(); i++) {
1394 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001395 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -07001396 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001397 }
Christopher Tate7060b042014-06-09 19:50:00 -07001398 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001399 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -07001400 }
1401
1402 /**
1403 * Entry point from client to cancel the job corresponding to the jobId provided.
1404 * This will remove the job from the master list, and cancel the job if it was staged for
1405 * execution or being executed.
1406 * @param uid Uid of the calling client.
1407 * @param jobId Id of the job, provided at schedule-time.
1408 */
Makoto Onukid2bfec62018-01-12 13:58:01 -08001409 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -07001410 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001411 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001412 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001413 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001414 cancelJobImplLocked(toCancel, null,
1415 "cancel() called by app, callingUid=" + callingUid
1416 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001417 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001418 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001419 }
1420 }
1421
Christopher Tate16032042018-10-05 15:53:50 -07001422 /**
1423 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob}
1424 * is null, the cancelled job is removed outright from the system. If
1425 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
1426 * currently scheduled jobs.
1427 */
Dianne Hackborn729a3282017-06-09 16:06:01 -07001428 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001429 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1430 cancelled.unprepareLocked(ActivityManager.getService());
1431 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1432 // Remove from pending queue.
1433 if (mPendingJobs.remove(cancelled)) {
1434 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001435 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001436 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001437 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Christopher Tate16032042018-10-05 15:53:50 -07001438 // If this is a replacement, bring in the new version of the job
1439 if (incomingJob != null) {
1440 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
1441 startTrackingJobLocked(incomingJob, cancelled);
1442 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001443 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001444 }
1445
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001446 void updateUidState(int uid, int procState) {
1447 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001448 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1449 // Only use this if we are exactly the top app. All others can live
1450 // with just the foreground priority. This means that persistent processes
1451 // can never be the top app priority... that is fine.
1452 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001453 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
1454 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001455 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001456 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001457 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001458 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001459 }
1460 }
1461 }
1462
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001463 @Override
1464 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001465 synchronized (mLock) {
Makoto Onuki0f6499c2018-12-14 16:34:59 -08001466 if (DEBUG) {
1467 Slog.d(TAG, "Doze state changed: " + deviceIdle);
1468 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001469 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001470 // When becoming idle, make sure no jobs are actively running,
1471 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001472 for (int i=0; i<mActiveServices.size(); i++) {
1473 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001474 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001475 if (executing != null
1476 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001477 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1478 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001479 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001480 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001481 } else {
1482 // When coming out of idle, allow thing to start back up.
1483 if (mReadyToRock) {
1484 if (mLocalDeviceIdleController != null) {
1485 if (!mReportedActive) {
1486 mReportedActive = true;
1487 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001488 }
1489 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001490 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001491 }
1492 }
1493 }
1494 }
1495
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001496 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001497 // active is true if pending queue contains jobs OR some job is running.
1498 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001499 if (mPendingJobs.size() <= 0) {
1500 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001501 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001502 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001503 if (job != null
1504 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001505 && !job.dozeWhitelisted
1506 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001507 // We will report active if we have a job running and it is not an exception
1508 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001509 active = true;
1510 break;
1511 }
1512 }
1513 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001514
1515 if (mReportedActive != active) {
1516 mReportedActive = active;
1517 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001518 mLocalDeviceIdleController.setJobsActive(active);
1519 }
1520 }
1521 }
1522
Christopher Tated117b292018-01-05 17:32:36 -08001523 void reportAppUsage(String packageName, int userId) {
1524 // This app just transitioned into interactive use or near equivalent, so we should
1525 // take a look at its job state for feedback purposes.
1526 }
1527
Christopher Tate7060b042014-06-09 19:50:00 -07001528 /**
1529 * Initializes the system service.
1530 * <p>
1531 * Subclasses must define a single argument constructor that accepts the context
1532 * and passes it to super.
1533 * </p>
1534 *
1535 * @param context The system server context.
1536 */
1537 public JobSchedulerService(Context context) {
1538 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001539
1540 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001541 mActivityManagerInternal = Preconditions.checkNotNull(
1542 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001543
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001544 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001545 mConstants = new Constants();
1546 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001547 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001548
Makoto Onuki714f97d2018-12-05 11:18:13 -08001549 mConcurrencyManager = new JobConcurrencyManager(this);
1550
Christopher Tatea732f012017-10-26 17:26:53 -07001551 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001552 mStandbyTracker = new StandbyTracker();
1553 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1554 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001555
1556 // The job store needs to call back
1557 publishLocalService(JobSchedulerInternal.class, new LocalService());
1558
1559 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001560 mJobs = JobStore.initAndGet(this);
1561
Christopher Tate7060b042014-06-09 19:50:00 -07001562 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001563 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001564 mControllers.add(new ConnectivityController(this));
1565 mControllers.add(new TimeController(this));
1566 mControllers.add(new IdleController(this));
1567 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001568 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001569 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001570 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001571 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001572 mControllers.add(new ContentObserverController(this));
1573 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001574 mControllers.add(mDeviceIdleJobsController);
Kweku Adams4836f9d2018-11-12 17:04:17 -08001575 mControllers.add(new QuotaController(this));
Christopher Tate616541d2017-07-26 14:27:38 -07001576
1577 // If the job store determined that it can't yet reschedule persisted jobs,
1578 // we need to start watching the clock.
1579 if (!mJobs.jobTimesInflatedValid()) {
1580 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1581 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1582 }
Christopher Tate7060b042014-06-09 19:50:00 -07001583 }
1584
Christopher Tate616541d2017-07-26 14:27:38 -07001585 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1586 @Override
1587 public void onReceive(Context context, Intent intent) {
1588 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1589 // When we reach clock sanity, recalculate the temporal windows
1590 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001591 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001592 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1593
1594 // We've done our job now, so stop watching the time.
1595 context.unregisterReceiver(this);
1596
1597 // And kick off the work to update the affected jobs, using a secondary
1598 // thread instead of chugging away here on the main looper thread.
1599 FgThread.getHandler().post(mJobTimeUpdater);
1600 }
1601 }
1602 }
1603 };
1604
1605 private final Runnable mJobTimeUpdater = () -> {
1606 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1607 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1608 synchronized (mLock) {
1609 // Note: we intentionally both look up the existing affected jobs and replace them
1610 // with recalculated ones inside the same lock lifetime.
1611 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1612
1613 // Now, at each position [i], we have both the existing JobStatus
1614 // and the one that replaces it.
1615 final int N = toAdd.size();
1616 for (int i = 0; i < N; i++) {
1617 final JobStatus oldJob = toRemove.get(i);
1618 final JobStatus newJob = toAdd.get(i);
1619 if (DEBUG) {
1620 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1621 }
1622 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1623 }
1624 }
1625 };
1626
Christopher Tate7060b042014-06-09 19:50:00 -07001627 @Override
1628 public void onStart() {
1629 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1630 }
1631
1632 @Override
1633 public void onBootPhase(int phase) {
1634 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001635 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001636
Makoto Onukie4918212018-02-06 11:30:15 -08001637 mAppStateTracker = Preconditions.checkNotNull(
1638 LocalServices.getService(AppStateTracker.class));
Kweku Adams4836f9d2018-11-12 17:04:17 -08001639 if (mConstants.USE_HEARTBEATS) {
1640 setNextHeartbeatAlarm();
1641 }
Makoto Onuki15407842018-01-19 14:23:11 -08001642
Shreyas Basarge5db09082016-01-07 13:38:29 +00001643 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001644 final IntentFilter filter = new IntentFilter();
1645 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1646 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001647 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1648 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001649 filter.addDataScheme("package");
1650 getContext().registerReceiverAsUser(
1651 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1652 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1653 getContext().registerReceiverAsUser(
1654 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001655 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001656 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001657 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001658 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1659 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001660 } catch (RemoteException e) {
1661 // ignored; both services live in system_server
1662 }
Makoto Onukic279f2b2019-01-09 13:11:37 -08001663
1664 mConcurrencyManager.onSystemReady();
1665
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001666 // Remove any jobs that are not associated with any of the current users.
1667 cancelJobsForNonExistentUsers();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001668 // Register thermal callback
1669 mThermalService = IThermalService.Stub.asInterface(
1670 ServiceManager.getService(Context.THERMAL_SERVICE));
1671 if (mThermalService != null) {
1672 try {
1673 mThermalService.registerThermalStatusListener(new ThermalStatusListener());
1674 } catch (RemoteException e) {
1675 Slog.e(TAG, "Failed to register thermal callback.", e);
1676 }
1677 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001678 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001679 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001680 // Let's go!
1681 mReadyToRock = true;
1682 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1683 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001684 mLocalDeviceIdleController
1685 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001686 // Create the "runners".
1687 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1688 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001689 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001690 getContext().getMainLooper()));
1691 }
1692 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001693 mJobs.forEachJob((job) -> {
1694 for (int controller = 0; controller < mControllers.size(); controller++) {
1695 final StateController sc = mControllers.get(controller);
1696 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001697 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001698 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001699 // GO GO GO!
1700 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1701 }
Christopher Tate7060b042014-06-09 19:50:00 -07001702 }
1703 }
1704
1705 /**
1706 * Called when we have a job status object that we need to insert in our
1707 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1708 * about.
1709 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001710 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1711 if (!jobStatus.isPreparedLocked()) {
1712 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1713 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001714 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001715 final boolean update = mJobs.add(jobStatus);
1716 if (mReadyToRock) {
1717 for (int i = 0; i < mControllers.size(); i++) {
1718 StateController controller = mControllers.get(i);
1719 if (update) {
1720 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001721 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001722 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001723 }
Christopher Tate7060b042014-06-09 19:50:00 -07001724 }
1725 }
1726
1727 /**
1728 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1729 * object removed.
1730 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001731 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001732 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001733 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001734 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001735
1736 // Remove from store as well as controllers.
1737 final boolean removed = mJobs.remove(jobStatus, writeBack);
1738 if (removed && mReadyToRock) {
1739 for (int i=0; i<mControllers.size(); i++) {
1740 StateController controller = mControllers.get(i);
1741 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001742 }
1743 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001744 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001745 }
1746
Dianne Hackborn729a3282017-06-09 16:06:01 -07001747 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001748 for (int i=0; i<mActiveServices.size(); i++) {
1749 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001750 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001751 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001752 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001753 return true;
1754 }
1755 }
1756 return false;
1757 }
1758
1759 /**
1760 * @param job JobStatus we are querying against.
1761 * @return Whether or not the job represented by the status object is currently being run or
1762 * is pending.
1763 */
1764 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001765 for (int i=0; i<mActiveServices.size(); i++) {
1766 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001767 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001768 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1769 return true;
1770 }
1771 }
1772 return false;
1773 }
1774
Dianne Hackborn807de782016-04-07 17:54:41 -07001775 void noteJobsPending(List<JobStatus> jobs) {
1776 for (int i = jobs.size() - 1; i >= 0; i--) {
1777 JobStatus job = jobs.get(i);
1778 mJobPackageTracker.notePending(job);
1779 }
1780 }
1781
1782 void noteJobsNonpending(List<JobStatus> jobs) {
1783 for (int i = jobs.size() - 1; i >= 0; i--) {
1784 JobStatus job = jobs.get(i);
1785 mJobPackageTracker.noteNonpending(job);
1786 }
1787 }
1788
Christopher Tate7060b042014-06-09 19:50:00 -07001789 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001790 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1791 * specify an override deadline on a failed job (the failed job will run even though it's not
1792 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
Makoto Onukic279f2b2019-01-09 13:11:37 -08001793 * ready job with {@link JobStatus#getNumFailures()} > 0 will be executed.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001794 *
Christopher Tate7060b042014-06-09 19:50:00 -07001795 * @param failureToReschedule Provided job status that we will reschedule.
1796 * @return A newly instantiated JobStatus with the same constraints as the last job except
1797 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001798 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001799 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001800 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001801 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001802 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001803 final JobInfo job = failureToReschedule.getJob();
1804
1805 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001806 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1807 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001808
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001809 if (failureToReschedule.hasWorkLocked()) {
1810 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1811 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1812 + backoffAttempts + " > work limit "
1813 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1814 return null;
1815 }
1816 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1817 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1818 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1819 return null;
1820 }
1821
Christopher Tate7060b042014-06-09 19:50:00 -07001822 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001823 case JobInfo.BACKOFF_POLICY_LINEAR: {
1824 long backoff = initialBackoffMillis;
1825 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1826 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1827 }
1828 delayMillis = backoff * backoffAttempts;
1829 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001830 default:
1831 if (DEBUG) {
1832 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1833 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001834 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1835 long backoff = initialBackoffMillis;
1836 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1837 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1838 }
1839 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1840 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001841 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001842 delayMillis =
1843 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001844 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1845 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001846 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001847 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001848 for (int ic=0; ic<mControllers.size(); ic++) {
1849 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001850 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001851 }
1852 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001853 }
1854
1855 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001856 * Called after a periodic has executed so we can reschedule it. We take the last execution
1857 * time of the job to be the time of completion (i.e. the time at which this function is
1858 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001859 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001860 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1861 * to underscheduling at least, rather than if we had taken the last execution time to be the
1862 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001863 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1864 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001865 * @return A new job representing the execution criteria for this instantiation of the
1866 * recurring job.
1867 */
1868 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001869 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001870 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001871 long runEarly = 0L;
1872
1873 // If this periodic was rescheduled it won't have a deadline.
1874 if (periodicToReschedule.hasDeadlineConstraint()) {
1875 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1876 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001877 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001878 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001879 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1880 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001881
1882 if (DEBUG) {
1883 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1884 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1885 }
Christopher Tatea732f012017-10-26 17:26:53 -07001886 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1887 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1888 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001889 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001890 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001891 }
1892
Christopher Tate325768c2018-03-07 16:07:56 -08001893 /*
1894 * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1895 * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1896 * reboot behavior.
1897 */
Christopher Tated1aebb32018-01-31 13:24:14 -08001898 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
Christopher Tate325768c2018-03-07 16:07:56 -08001899 // The furthest back in pre-boot time that we need to bother with
1900 long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1901 boolean cacheHit = false;
Christopher Tated1aebb32018-01-31 13:24:14 -08001902 synchronized (mLock) {
Christopher Tate325768c2018-03-07 16:07:56 -08001903 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1904 if (jobPackages != null) {
1905 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1906 if (cachedValue < Long.MAX_VALUE) {
1907 cacheHit = true;
1908 heartbeat = cachedValue;
1909 }
1910 }
1911 if (!cacheHit) {
1912 // We haven't seen it yet; ask usage stats about it
1913 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1914 if (timeSinceJob < Long.MAX_VALUE) {
1915 // Usage stats knows about it from before, so calculate back from that
1916 // and go from there.
1917 heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1918 }
1919 // If usage stats returned its "not found" MAX_VALUE, we still have the
1920 // negative default 'heartbeat' value we established above
1921 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1922 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001923 }
1924 if (DEBUG_STANDBY) {
Christopher Tate325768c2018-03-07 16:07:56 -08001925 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1926 + packageName + "/" + userId);
Christopher Tated1aebb32018-01-31 13:24:14 -08001927 }
1928 return heartbeat;
1929 }
1930
1931 long heartbeatWhenJobsLastRun(JobStatus job) {
1932 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1933 }
1934
Christopher Tate325768c2018-03-07 16:07:56 -08001935 void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1936 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1937 if (jobPackages == null) {
1938 jobPackages = new HashMap<>();
1939 mLastJobHeartbeats.put(userId, jobPackages);
1940 }
1941 jobPackages.put(packageName, heartbeat);
1942 }
1943
Christopher Tate7060b042014-06-09 19:50:00 -07001944 // JobCompletedListener implementations.
1945
1946 /**
1947 * A job just finished executing. We fetch the
1948 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001949 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001950 * @param jobStatus Completed job.
1951 * @param needsReschedule Whether the implementing class should reschedule this job.
1952 */
1953 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001954 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001955 if (DEBUG) {
1956 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1957 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001958
1959 // If the job wants to be rescheduled, we first need to make the next upcoming
1960 // job so we can transfer any appropriate state over from the previous job when
1961 // we stop it.
1962 final JobStatus rescheduledJob = needsReschedule
1963 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1964
Shreyas Basarge73f10252016-02-11 17:06:13 +00001965 // Do not write back immediately if this is a periodic job. The job may get lost if system
1966 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001967 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001968 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001969 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001970 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001971 // We still want to check for jobs to execute, because this job may have
1972 // scheduled a new job under the same job id, and now we can run it.
1973 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001974 return;
1975 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001976
1977 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001978 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001979 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001980 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001981 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001982 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001983 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001984 } else if (jobStatus.getJob().isPeriodic()) {
1985 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001986 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001987 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001988 } catch (SecurityException e) {
1989 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1990 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001991 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001992 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001993 jobStatus.unprepareLocked(ActivityManager.getService());
1994 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001995 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001996 }
1997
1998 // StateChangedListener implementations.
1999
2000 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07002001 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
2002 * some controller's state has changed, so as to run through the list of jobs and start/stop
2003 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07002004 */
2005 @Override
2006 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07002007 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07002008 }
2009
2010 @Override
2011 public void onRunJobNow(JobStatus jobStatus) {
2012 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
2013 }
2014
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002015 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07002016
2017 public JobHandler(Looper looper) {
2018 super(looper);
2019 }
2020
2021 @Override
2022 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002023 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07002024 if (!mReadyToRock) {
2025 return;
2026 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002027 switch (message.what) {
2028 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07002029 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07002030 // runNow can be null, which is a controller's way of indicating that its
2031 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07002032 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002033 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002034 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002035 } else {
2036 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002037 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002038 } break;
2039 case MSG_CHECK_JOB:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08002040 if (DEBUG) {
2041 Slog.d(TAG, "MSG_CHECK_JOB");
2042 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002043 if (mReportedActive) {
2044 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002045 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002046 } else {
2047 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002048 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002049 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002050 break;
2051 case MSG_CHECK_JOB_GREEDY:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08002052 if (DEBUG) {
2053 Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
2054 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002055 queueReadyJobsForExecutionLocked();
2056 break;
2057 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07002058 cancelJobImplLocked((JobStatus) message.obj, null,
2059 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002060 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08002061
2062 case MSG_UID_STATE_CHANGED: {
2063 final int uid = message.arg1;
2064 final int procState = message.arg2;
2065 updateUidState(uid, procState);
2066 break;
2067 }
2068 case MSG_UID_GONE: {
2069 final int uid = message.arg1;
2070 final boolean disabled = message.arg2 != 0;
2071 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
2072 if (disabled) {
2073 cancelJobsForUid(uid, "uid gone");
2074 }
2075 synchronized (mLock) {
2076 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2077 }
2078 break;
2079 }
2080 case MSG_UID_ACTIVE: {
2081 final int uid = message.arg1;
2082 synchronized (mLock) {
2083 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
2084 }
2085 break;
2086 }
2087 case MSG_UID_IDLE: {
2088 final int uid = message.arg1;
2089 final boolean disabled = message.arg2 != 0;
2090 if (disabled) {
2091 cancelJobsForUid(uid, "app uid idle");
2092 }
2093 synchronized (mLock) {
2094 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2095 }
2096 break;
2097 }
2098
Matthew Williams75fc5252014-09-02 16:17:53 -07002099 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002100 maybeRunPendingJobsLocked();
2101 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
2102 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07002103 }
2104 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002105 }
Christopher Tate7060b042014-06-09 19:50:00 -07002106
Wei Wang8c0c3c12018-11-14 14:56:52 -08002107 private boolean isJobThermalConstrainedLocked(JobStatus job) {
2108 return mThermalConstraint && job.hasConnectivityConstraint()
2109 && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
2110 }
2111
Dianne Hackborn6d068262017-05-16 13:14:37 -07002112 private void stopNonReadyActiveJobsLocked() {
2113 for (int i=0; i<mActiveServices.size(); i++) {
2114 JobServiceContext serviceContext = mActiveServices.get(i);
2115 final JobStatus running = serviceContext.getRunningJobLocked();
Wei Wang8c0c3c12018-11-14 14:56:52 -08002116 if (running == null) {
2117 continue;
2118 }
2119 if (!running.isReady()) {
Dianne Hackborn6d068262017-05-16 13:14:37 -07002120 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07002121 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
2122 "cancelled due to unsatisfied constraints");
Wei Wang8c0c3c12018-11-14 14:56:52 -08002123 } else if (isJobThermalConstrainedLocked(running)) {
2124 serviceContext.cancelExecutingJobLocked(
2125 JobParameters.REASON_DEVICE_THERMAL,
2126 "cancelled due to thermal condition");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002127 }
2128 }
2129 }
2130
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002131 /**
2132 * Run through list of jobs and execute all possible - at least one is expired so we do
2133 * as many as we can.
2134 */
2135 private void queueReadyJobsForExecutionLocked() {
2136 if (DEBUG) {
2137 Slog.d(TAG, "queuing all ready jobs for execution:");
2138 }
2139 noteJobsNonpending(mPendingJobs);
2140 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002141 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002142 mJobs.forEachJob(mReadyQueueFunctor);
2143 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08002144
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002145 if (DEBUG) {
2146 final int queuedJobs = mPendingJobs.size();
2147 if (queuedJobs == 0) {
2148 Slog.d(TAG, "No jobs pending.");
2149 } else {
2150 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08002151 }
2152 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002153 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002154
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002155 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002156 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08002157
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002158 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002159 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002160 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07002161 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002162 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07002163 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002164 if (newReadyJobs == null) {
2165 newReadyJobs = new ArrayList<JobStatus>();
2166 }
2167 newReadyJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002168 } else {
2169 evaluateControllerStatesLocked(job);
Christopher Tate7060b042014-06-09 19:50:00 -07002170 }
2171 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002172
2173 public void postProcess() {
2174 if (newReadyJobs != null) {
2175 noteJobsPending(newReadyJobs);
2176 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002177 if (mPendingJobs.size() > 1) {
2178 mPendingJobs.sort(mEnqueueTimeComparator);
2179 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002180 }
2181 newReadyJobs = null;
2182 }
2183 }
2184 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
2185
2186 /**
2187 * The state of at least one job has changed. Here is where we could enforce various
2188 * policies on when we want to execute jobs.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002189 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002190 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002191 int chargingCount;
2192 int batteryNotLowCount;
2193 int storageNotLowCount;
2194 int idleCount;
2195 int backoffCount;
2196 int connectivityCount;
2197 int contentCount;
2198 List<JobStatus> runnableJobs;
2199
2200 public MaybeReadyJobQueueFunctor() {
2201 reset();
2202 }
2203
2204 // Functor method invoked for each job via JobStore.forEachJob()
2205 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002206 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002207 if (isReadyToBeExecutedLocked(job)) {
2208 try {
2209 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
2210 job.getJob().getService().getPackageName())) {
2211 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
2212 + job.getJob().toString() + " -- package not allowed to start");
2213 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
2214 return;
2215 }
2216 } catch (RemoteException e) {
2217 }
2218 if (job.getNumFailures() > 0) {
2219 backoffCount++;
2220 }
2221 if (job.hasIdleConstraint()) {
2222 idleCount++;
2223 }
2224 if (job.hasConnectivityConstraint()) {
2225 connectivityCount++;
2226 }
2227 if (job.hasChargingConstraint()) {
2228 chargingCount++;
2229 }
2230 if (job.hasBatteryNotLowConstraint()) {
2231 batteryNotLowCount++;
2232 }
2233 if (job.hasStorageNotLowConstraint()) {
2234 storageNotLowCount++;
2235 }
2236 if (job.hasContentTriggerConstraint()) {
2237 contentCount++;
2238 }
2239 if (runnableJobs == null) {
2240 runnableJobs = new ArrayList<>();
2241 }
2242 runnableJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002243 } else {
2244 evaluateControllerStatesLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002245 }
2246 }
2247
2248 public void postProcess() {
2249 if (backoffCount > 0 ||
2250 idleCount >= mConstants.MIN_IDLE_COUNT ||
2251 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
2252 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
2253 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
2254 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
2255 contentCount >= mConstants.MIN_CONTENT_COUNT ||
2256 (runnableJobs != null
2257 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
2258 if (DEBUG) {
2259 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
2260 }
2261 noteJobsPending(runnableJobs);
2262 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002263 if (mPendingJobs.size() > 1) {
2264 mPendingJobs.sort(mEnqueueTimeComparator);
2265 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002266 } else {
2267 if (DEBUG) {
2268 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
2269 }
2270 }
2271
2272 // Be ready for next time
2273 reset();
2274 }
2275
2276 private void reset() {
2277 chargingCount = 0;
2278 idleCount = 0;
2279 backoffCount = 0;
2280 connectivityCount = 0;
2281 batteryNotLowCount = 0;
2282 storageNotLowCount = 0;
2283 contentCount = 0;
2284 runnableJobs = null;
2285 }
2286 }
2287 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
2288
2289 private void maybeQueueReadyJobsForExecutionLocked() {
2290 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
2291
2292 noteJobsNonpending(mPendingJobs);
2293 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002294 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002295 mJobs.forEachJob(mMaybeQueueFunctor);
2296 mMaybeQueueFunctor.postProcess();
2297 }
2298
Christopher Tated1aebb32018-01-31 13:24:14 -08002299 /**
2300 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
2301 */
2302 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
2303
2304 @Override
2305 public void onAlarm() {
2306 synchronized (mLock) {
2307 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
2308 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
2309 if (beatsElapsed > 0) {
2310 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
2311 advanceHeartbeatLocked(beatsElapsed);
2312 }
Christopher Tatea732f012017-10-26 17:26:53 -07002313 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002314 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07002315 }
Christopher Tatea732f012017-10-26 17:26:53 -07002316 }
2317
Christopher Tated1aebb32018-01-31 13:24:14 -08002318 // Intentionally does not touch the alarm timing
2319 void advanceHeartbeatLocked(long beatsElapsed) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002320 if (!mConstants.USE_HEARTBEATS) {
2321 return;
2322 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002323 mHeartbeat += beatsElapsed;
2324 if (DEBUG_STANDBY) {
2325 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
2326 + " to " + mHeartbeat);
2327 }
2328 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
2329 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
2330 // new jobs scheduled by apps in that bucket will be permitted to run
2331 // immediately.
2332 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07002333 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002334 // Did we reach or cross a bucket boundary?
2335 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
2336 didAdvanceBucket = true;
2337 }
2338 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07002339 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
2340 }
2341 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002342 Slog.v(TAG, " Bucket " + i + " next heartbeat "
2343 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07002344 }
2345 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002346
2347 if (didAdvanceBucket) {
2348 if (DEBUG_STANDBY) {
2349 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
2350 }
2351 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2352 }
2353 }
2354
2355 void setNextHeartbeatAlarm() {
2356 final long heartbeatLength;
2357 synchronized (mLock) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002358 if (!mConstants.USE_HEARTBEATS) {
2359 return;
2360 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002361 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
2362 }
2363 final long now = sElapsedRealtimeClock.millis();
2364 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
2365 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
2366 if (DEBUG_STANDBY) {
2367 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
2368 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
2369 }
2370 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
2371 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
2372 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07002373 }
2374
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002375 /** Returns true if both the calling and source users for the job are started. */
2376 private boolean areUsersStartedLocked(final JobStatus job) {
2377 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
2378 if (job.getUserId() == job.getSourceUserId()) {
2379 return sourceStarted;
2380 }
2381 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
2382 }
2383
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002384 /**
2385 * Criteria for moving a job into the pending queue:
2386 * - It's ready.
2387 * - It's not pending.
2388 * - It's not already running on a JSC.
2389 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07002390 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002391 * - The component is enabled and runnable.
2392 */
2393 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002394 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002395
2396 if (DEBUG) {
2397 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2398 + " ready=" + jobReady);
2399 }
2400
2401 // This is a condition that is very likely to be false (most jobs that are
2402 // scheduled are sitting there, not ready yet) and very cheap to check (just
2403 // a few conditions on data in JobStatus).
2404 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08002405 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
2406 Slog.v(TAG, " NOT READY: " + job);
2407 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002408 return false;
2409 }
2410
2411 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002412
Kweku Adams8bd5edc2018-12-07 18:33:39 -08002413 final boolean userStarted = areUsersStartedLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002414
2415 if (DEBUG) {
2416 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002417 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002418 }
2419
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002420 // These are also fairly cheap to check, though they typically will not
2421 // be conditions we fail.
2422 if (!jobExists || !userStarted) {
2423 return false;
2424 }
2425
Wei Wang8c0c3c12018-11-14 14:56:52 -08002426 if (isJobThermalConstrainedLocked(job)) {
2427 return false;
2428 }
2429
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002430 final boolean jobPending = mPendingJobs.contains(job);
2431 final boolean jobActive = isCurrentlyActiveLocked(job);
2432
2433 if (DEBUG) {
2434 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2435 + " pending=" + jobPending + " active=" + jobActive);
2436 }
2437
2438 // These can be a little more expensive (especially jobActive, since we need to
2439 // go through the array of all potentially active jobs), so we are doing them
2440 // later... but still before checking with the package manager!
2441 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002442 return false;
2443 }
2444
Kweku Adams4836f9d2018-11-12 17:04:17 -08002445 if (mConstants.USE_HEARTBEATS) {
2446 // If the app is in a non-active standby bucket, make sure we've waited
2447 // an appropriate amount of time since the last invocation. During device-
2448 // wide parole, standby bucketing is ignored.
2449 //
2450 // Jobs in 'active' apps are not subject to standby, nor are jobs that are
2451 // specifically marked as exempt.
Christopher Tate20afddd2018-02-28 15:19:19 -08002452 if (DEBUG_STANDBY) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002453 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2454 + " parole=" + mInParole + " active=" + job.uidActive
2455 + " exempt=" + job.getJob().isExemptedFromAppStandby());
Christopher Tate20afddd2018-02-28 15:19:19 -08002456 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002457 if (!mInParole
2458 && !job.uidActive
2459 && !job.getJob().isExemptedFromAppStandby()) {
2460 final int bucket = job.getStandbyBucket();
2461 if (DEBUG_STANDBY) {
2462 Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
2463 + " next=" + mNextBucketHeartbeat[bucket]);
2464 }
2465 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
2466 // Only skip this job if the app is still waiting for the end of its nominal
2467 // bucket interval. Once it's waited that long, we let it go ahead and clear.
2468 // The final (NEVER) bucket is special; we never age those apps' jobs into
2469 // runnability.
2470 final long appLastRan = heartbeatWhenJobsLastRun(job);
2471 if (bucket >= mConstants.STANDBY_BEATS.length
2472 || (mHeartbeat > appLastRan
2473 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
2474 // TODO: log/trace that we're deferring the job due to bucketing if we
2475 // hit this
2476 if (job.getWhenStandbyDeferred() == 0) {
2477 if (DEBUG_STANDBY) {
2478 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
2479 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2480 + " for " + job);
2481 }
2482 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002483 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002484 return false;
2485 } else {
2486 if (DEBUG_STANDBY) {
2487 Slog.v(TAG, "Bucket deferred job aged into runnability at "
2488 + mHeartbeat + " : " + job);
2489 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002490 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08002491 }
Christopher Tatea732f012017-10-26 17:26:53 -07002492 }
Christopher Tatea732f012017-10-26 17:26:53 -07002493 }
2494
2495 // The expensive check last: validate that the defined package+service is
2496 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002497 final boolean componentPresent;
2498 try {
2499 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2500 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
Kweku Adams8bd5edc2018-12-07 18:33:39 -08002501 job.getUserId()) != null);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002502 } catch (RemoteException e) {
2503 throw e.rethrowAsRuntimeException();
2504 }
2505
2506 if (DEBUG) {
2507 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2508 + " componentPresent=" + componentPresent);
2509 }
2510
2511 // Everything else checked out so far, so this is the final yes/no check
2512 return componentPresent;
2513 }
2514
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002515 private void evaluateControllerStatesLocked(final JobStatus job) {
2516 for (int c = mControllers.size() - 1; c >= 0; --c) {
2517 final StateController sc = mControllers.get(c);
2518 sc.evaluateStateLocked(job);
2519 }
2520 }
2521
2522 /**
2523 * Returns true if non-job constraint components are in place -- if job.isReady() returns true
2524 * and this method returns true, then the job is ready to be executed.
2525 */
2526 public boolean areComponentsInPlaceLocked(JobStatus job) {
2527 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
2528 // conditions.
2529
2530 final boolean jobExists = mJobs.containsJob(job);
2531 final boolean userStarted = areUsersStartedLocked(job);
2532
2533 if (DEBUG) {
2534 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2535 + " exists=" + jobExists + " userStarted=" + userStarted);
2536 }
2537
2538 // These are also fairly cheap to check, though they typically will not
2539 // be conditions we fail.
2540 if (!jobExists || !userStarted) {
2541 return false;
2542 }
2543
2544 // Job pending/active doesn't affect the readiness of a job.
2545
2546 // Skipping the hearbeat check as this will only come into play when using the rolling
2547 // window quota management system.
2548
2549 // The expensive check last: validate that the defined package+service is
2550 // still present & viable.
2551 final boolean componentPresent;
2552 try {
2553 // TODO: cache result until we're notified that something in the package changed.
2554 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2555 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2556 job.getUserId()) != null);
2557 } catch (RemoteException e) {
2558 throw e.rethrowAsRuntimeException();
2559 }
2560
2561 if (DEBUG) {
2562 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2563 + " componentPresent=" + componentPresent);
2564 }
2565
2566 // Everything else checked out so far, so this is the final yes/no check
2567 return componentPresent;
2568 }
2569
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002570 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002571 * Reconcile jobs in the pending queue against available execution contexts.
2572 * A controller can force a job into the pending queue even if it's already running, but
2573 * here is where we decide whether to actually execute it.
2574 */
Makoto Onukic279f2b2019-01-09 13:11:37 -08002575 void maybeRunPendingJobsLocked() {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002576 if (DEBUG) {
2577 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2578 }
Makoto Onuki714f97d2018-12-05 11:18:13 -08002579 mConcurrencyManager.assignJobsToContextsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002580 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002581 }
2582
Dianne Hackborn807de782016-04-07 17:54:41 -07002583 private int adjustJobPriority(int curPriority, JobStatus job) {
2584 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2585 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002586 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002587 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002588 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002589 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2590 }
2591 }
2592 return curPriority;
2593 }
2594
Makoto Onuki714f97d2018-12-05 11:18:13 -08002595 int evaluateJobPriorityLocked(JobStatus job) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002596 int priority = job.getPriority();
Makoto Onukiec8b14d2018-12-05 13:22:24 -08002597 if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002598 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002599 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002600 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2601 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002602 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002603 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002604 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002605 }
2606
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002607 final class LocalService implements JobSchedulerInternal {
2608
2609 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002610 * The current bucket heartbeat ordinal
2611 */
2612 public long currentHeartbeat() {
2613 return getCurrentHeartbeat();
2614 }
2615
2616 /**
2617 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2618 */
2619 public long nextHeartbeatForBucket(int bucket) {
2620 synchronized (mLock) {
2621 return mNextBucketHeartbeat[bucket];
2622 }
2623 }
2624
2625 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002626 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2627 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2628 * jobs in a long time is immediately runnable even if the app is bucketed into
2629 * an infrequent time allocation.
2630 */
2631 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2632 final int appStandbyBucket) {
2633 if (appStandbyBucket == 0 ||
2634 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2635 // ACTIVE => everything can be run right away
2636 // NEVER => we won't run them anyway, so let them go in the future
2637 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002638 if (DEBUG_STANDBY) {
2639 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2640 + packageName + "/" + userId);
2641 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002642 return 0;
2643 }
2644
Christopher Tated1aebb32018-01-31 13:24:14 -08002645 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2646 if (DEBUG_STANDBY) {
2647 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2648 + packageName + "/" + userId);
2649 }
2650 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002651 }
2652
Christopher Tate325768c2018-03-07 16:07:56 -08002653 public void noteJobStart(String packageName, int userId) {
2654 synchronized (mLock) {
2655 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2656 }
2657 }
2658
Christopher Tate435c2f42018-01-18 12:59:15 -08002659 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002660 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2661 * jobs are always considered pending.
2662 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002663 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002664 public List<JobInfo> getSystemScheduledPendingJobs() {
2665 synchronized (mLock) {
2666 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002667 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2668 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2669 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002670 }
2671 });
2672 return pendingJobs;
2673 }
2674 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002675
2676 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002677 public void cancelJobsForUid(int uid, String reason) {
2678 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2679 }
2680
2681 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002682 public void addBackingUpUid(int uid) {
2683 synchronized (mLock) {
2684 // No need to actually do anything here, since for a full backup the
2685 // activity manager will kill the process which will kill the job (and
2686 // cause it to restart, but now it can't run).
2687 mBackingUpUids.put(uid, uid);
2688 }
2689 }
2690
2691 @Override
2692 public void removeBackingUpUid(int uid) {
2693 synchronized (mLock) {
2694 mBackingUpUids.delete(uid);
2695 // If there are any jobs for this uid, we need to rebuild the pending list
2696 // in case they are now ready to run.
2697 if (mJobs.countJobsForUid(uid) > 0) {
2698 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2699 }
2700 }
2701 }
2702
2703 @Override
2704 public void clearAllBackingUpUids() {
2705 synchronized (mLock) {
2706 if (mBackingUpUids.size() > 0) {
2707 mBackingUpUids.clear();
2708 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2709 }
2710 }
2711 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002712
2713 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002714 public void reportAppUsage(String packageName, int userId) {
2715 JobSchedulerService.this.reportAppUsage(packageName, userId);
2716 }
2717
2718 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002719 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002720 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002721 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002722 }
2723 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002724 }
2725
Shreyas Basarge5db09082016-01-07 13:38:29 +00002726 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002727 * Tracking of app assignments to standby buckets
2728 */
2729 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002730
Christopher Tatea732f012017-10-26 17:26:53 -07002731 // AppIdleStateChangeListener interface for live updates
2732
2733 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002734 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002735 boolean idle, int bucket, int reason) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002736 // QuotaController handles this now.
Christopher Tatea732f012017-10-26 17:26:53 -07002737 }
2738
2739 @Override
2740 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002741 if (DEBUG_STANDBY) {
2742 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2743 }
2744 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002745 }
Christopher Tated117b292018-01-05 17:32:36 -08002746
2747 @Override
2748 public void onUserInteractionStarted(String packageName, int userId) {
2749 final int uid = mLocalPM.getPackageUid(packageName,
2750 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2751 if (uid < 0) {
2752 // Quietly ignore; the case is already logged elsewhere
2753 return;
2754 }
2755
Amith Yamasani977e11f2018-02-16 11:29:54 -08002756 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2757 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2758 // Too long ago, not worth logging
2759 sinceLast = 0L;
2760 }
Christopher Tated117b292018-01-05 17:32:36 -08002761 final DeferredJobCounter counter = new DeferredJobCounter();
2762 synchronized (mLock) {
2763 mJobs.forEachJobForSourceUid(uid, counter);
2764 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002765 if (counter.numDeferred() > 0 || sinceLast > 0) {
2766 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2767 (BatteryStatsInternal.class);
2768 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
Yangster-mac96353002018-09-05 11:18:55 -07002769 StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
2770 counter.numDeferred(), sinceLast);
Amith Yamasani977e11f2018-02-16 11:29:54 -08002771 }
Christopher Tated117b292018-01-05 17:32:36 -08002772 }
2773 }
2774
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002775 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002776 private int mDeferred = 0;
2777
2778 public int numDeferred() {
2779 return mDeferred;
2780 }
2781
2782 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002783 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002784 if (job.getWhenStandbyDeferred() > 0) {
2785 mDeferred++;
2786 }
2787 }
Christopher Tatea732f012017-10-26 17:26:53 -07002788 }
2789
2790 public static int standbyBucketToBucketIndex(int bucket) {
2791 // Normalize AppStandby constants to indices into our bookkeeping
Christopher Tatef2159712018-03-27 16:04:14 -07002792 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2793 else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2794 else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2795 else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2796 else return ACTIVE_INDEX;
Christopher Tatea732f012017-10-26 17:26:53 -07002797 }
2798
Christopher Tated1aebb32018-01-31 13:24:14 -08002799 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002800 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2801 UsageStatsManagerInternal usageStats = LocalServices.getService(
2802 UsageStatsManagerInternal.class);
2803 int bucket = usageStats != null
2804 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2805 : 0;
2806
2807 bucket = standbyBucketToBucketIndex(bucket);
2808
2809 if (DEBUG_STANDBY) {
2810 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2811 }
2812 return bucket;
2813 }
2814
2815 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002816 * Binder stub trampoline implementation
2817 */
2818 final class JobSchedulerStub extends IJobScheduler.Stub {
2819 /** Cache determination of whether a given app can persist jobs
2820 * key is uid of the calling app; value is undetermined/true/false
2821 */
2822 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2823
2824 // Enforce that only the app itself (or shared uid participant) can schedule a
2825 // job that runs one of the app's services, as well as verifying that the
2826 // named service properly requires the BIND_JOB_SERVICE permission
2827 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002828 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002829 final ComponentName service = job.getService();
2830 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002831 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002832 PackageManager.MATCH_DIRECT_BOOT_AWARE
2833 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002834 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002835 if (si == null) {
2836 throw new IllegalArgumentException("No such service " + service);
2837 }
Christopher Tate7060b042014-06-09 19:50:00 -07002838 if (si.applicationInfo.uid != uid) {
2839 throw new IllegalArgumentException("uid " + uid +
2840 " cannot schedule job in " + service.getPackageName());
2841 }
2842 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2843 throw new IllegalArgumentException("Scheduled service " + service
2844 + " does not require android.permission.BIND_JOB_SERVICE permission");
2845 }
Christopher Tate5568f542014-06-18 13:53:31 -07002846 } catch (RemoteException e) {
2847 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002848 }
2849 }
2850
2851 private boolean canPersistJobs(int pid, int uid) {
2852 // If we get this far we're good to go; all we need to do now is check
2853 // whether the app is allowed to persist its scheduled work.
2854 final boolean canPersist;
2855 synchronized (mPersistCache) {
2856 Boolean cached = mPersistCache.get(uid);
2857 if (cached != null) {
2858 canPersist = cached.booleanValue();
2859 } else {
2860 // Persisting jobs is tantamount to running at boot, so we permit
2861 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2862 // permission
2863 int result = getContext().checkPermission(
2864 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2865 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2866 mPersistCache.put(uid, canPersist);
2867 }
2868 }
2869 return canPersist;
2870 }
2871
Makoto Onuki959acb52018-01-26 14:10:03 -08002872 private void validateJobFlags(JobInfo job, int callingUid) {
2873 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2874 getContext().enforceCallingOrSelfPermission(
2875 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2876 }
2877 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2878 if (callingUid != Process.SYSTEM_UID) {
2879 throw new SecurityException("Job has invalid flags");
2880 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002881 if (job.isPeriodic()) {
2882 Slog.wtf(TAG, "Periodic jobs mustn't have"
2883 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002884 }
2885 }
2886 }
2887
Christopher Tate7060b042014-06-09 19:50:00 -07002888 // IJobScheduler implementation
2889 @Override
2890 public int schedule(JobInfo job) throws RemoteException {
2891 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002892 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002893 }
2894 final int pid = Binder.getCallingPid();
2895 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002896 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002897
2898 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002899 if (job.isPersisted()) {
2900 if (!canPersistJobs(pid, uid)) {
2901 throw new IllegalArgumentException("Error: requested job be persisted without"
2902 + " holding RECEIVE_BOOT_COMPLETED permission.");
2903 }
2904 }
Christopher Tate7060b042014-06-09 19:50:00 -07002905
Makoto Onuki959acb52018-01-26 14:10:03 -08002906 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002907
Christopher Tate7060b042014-06-09 19:50:00 -07002908 long ident = Binder.clearCallingIdentity();
2909 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002910 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2911 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002912 } finally {
2913 Binder.restoreCallingIdentity(ident);
2914 }
2915 }
2916
2917 // IJobScheduler implementation
2918 @Override
2919 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2920 if (DEBUG) {
2921 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2922 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002923 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002924 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002925
2926 enforceValidJobRequest(uid, job);
2927 if (job.isPersisted()) {
2928 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2929 }
2930 if (work == null) {
2931 throw new NullPointerException("work is null");
2932 }
2933
Makoto Onuki959acb52018-01-26 14:10:03 -08002934 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002935
2936 long ident = Binder.clearCallingIdentity();
2937 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002938 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2939 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002940 } finally {
2941 Binder.restoreCallingIdentity(ident);
2942 }
2943 }
2944
2945 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002946 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002947 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002948 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002949 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002950 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002951 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002952 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002953
2954 if (packageName == null) {
2955 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002956 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002957
2958 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2959 android.Manifest.permission.UPDATE_DEVICE_STATS);
2960 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2961 throw new SecurityException("Caller uid " + callerUid
2962 + " not permitted to schedule jobs for other apps");
2963 }
2964
Makoto Onuki959acb52018-01-26 14:10:03 -08002965 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002966
Shreyas Basarge968ac752016-01-11 23:09:26 +00002967 long ident = Binder.clearCallingIdentity();
2968 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002969 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002970 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002971 } finally {
2972 Binder.restoreCallingIdentity(ident);
2973 }
2974 }
2975
2976 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002977 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2978 final int uid = Binder.getCallingUid();
2979
2980 long ident = Binder.clearCallingIdentity();
2981 try {
2982 return JobSchedulerService.this.getPendingJobs(uid);
2983 } finally {
2984 Binder.restoreCallingIdentity(ident);
2985 }
2986 }
2987
2988 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002989 public JobInfo getPendingJob(int jobId) throws RemoteException {
2990 final int uid = Binder.getCallingUid();
2991
2992 long ident = Binder.clearCallingIdentity();
2993 try {
2994 return JobSchedulerService.this.getPendingJob(uid, jobId);
2995 } finally {
2996 Binder.restoreCallingIdentity(ident);
2997 }
2998 }
2999
3000 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07003001 public void cancelAll() throws RemoteException {
3002 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07003003 long ident = Binder.clearCallingIdentity();
3004 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08003005 JobSchedulerService.this.cancelJobsForUid(uid,
3006 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07003007 } finally {
3008 Binder.restoreCallingIdentity(ident);
3009 }
3010 }
3011
3012 @Override
3013 public void cancel(int jobId) throws RemoteException {
3014 final int uid = Binder.getCallingUid();
3015
3016 long ident = Binder.clearCallingIdentity();
3017 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08003018 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07003019 } finally {
3020 Binder.restoreCallingIdentity(ident);
3021 }
3022 }
3023
3024 /**
3025 * "dumpsys" infrastructure
3026 */
3027 @Override
3028 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06003029 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07003030
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003031 int filterUid = -1;
3032 boolean proto = false;
3033 if (!ArrayUtils.isEmpty(args)) {
3034 int opti = 0;
3035 while (opti < args.length) {
3036 String arg = args[opti];
3037 if ("-h".equals(arg)) {
3038 dumpHelp(pw);
3039 return;
3040 } else if ("-a".equals(arg)) {
3041 // Ignore, we always dump all.
3042 } else if ("--proto".equals(arg)) {
3043 proto = true;
3044 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
3045 pw.println("Unknown option: " + arg);
3046 return;
3047 } else {
3048 break;
3049 }
3050 opti++;
3051 }
3052 if (opti < args.length) {
3053 String pkg = args[opti];
3054 try {
3055 filterUid = getContext().getPackageManager().getPackageUid(pkg,
3056 PackageManager.MATCH_ANY_USER);
3057 } catch (NameNotFoundException ignored) {
3058 pw.println("Invalid package: " + pkg);
3059 return;
3060 }
3061 }
3062 }
3063
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003064 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07003065 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003066 if (proto) {
3067 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
3068 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003069 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
3070 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003071 }
Christopher Tate7060b042014-06-09 19:50:00 -07003072 } finally {
3073 Binder.restoreCallingIdentity(identityToken);
3074 }
3075 }
Christopher Tate5d346052016-03-08 12:56:08 -08003076
3077 @Override
3078 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07003079 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08003080 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07003081 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08003082 }
Serik Beketayev75915d12018-08-01 16:56:59 -07003083
3084 /**
3085 * <b>For internal system user only!</b>
3086 * Returns a list of all currently-executing jobs.
3087 */
3088 @Override
3089 public List<JobInfo> getStartedJobs() {
3090 final int uid = Binder.getCallingUid();
3091 if (uid != Process.SYSTEM_UID) {
3092 throw new SecurityException(
3093 "getStartedJobs() is system internal use only.");
3094 }
3095
3096 final ArrayList<JobInfo> runningJobs;
3097
3098 synchronized (mLock) {
3099 runningJobs = new ArrayList<>(mActiveServices.size());
3100 for (JobServiceContext jsc : mActiveServices) {
3101 final JobStatus job = jsc.getRunningJobLocked();
3102 if (job != null) {
3103 runningJobs.add(job.getJob());
3104 }
3105 }
3106 }
3107
3108 return runningJobs;
3109 }
3110
3111 /**
3112 * <b>For internal system user only!</b>
3113 * Returns a snapshot of the state of all jobs known to the system.
3114 *
3115 * <p class="note">This is a slow operation, so it should be called sparingly.
3116 */
3117 @Override
3118 public List<JobSnapshot> getAllJobSnapshots() {
3119 final int uid = Binder.getCallingUid();
3120 if (uid != Process.SYSTEM_UID) {
3121 throw new SecurityException(
3122 "getAllJobSnapshots() is system internal use only.");
3123 }
3124 synchronized (mLock) {
3125 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
3126 mJobs.forEachJob((job) -> snapshots.add(
3127 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
3128 isReadyToBeExecutedLocked(job))));
3129 return snapshots;
3130 }
3131 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00003132 };
3133
Christopher Tate5d346052016-03-08 12:56:08 -08003134 // Shell command infrastructure: run the given job immediately
3135 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
3136 if (DEBUG) {
3137 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
3138 + " " + jobId + " f=" + force);
3139 }
3140
3141 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003142 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3143 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08003144 if (uid < 0) {
3145 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3146 }
3147
3148 synchronized (mLock) {
3149 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3150 if (js == null) {
3151 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3152 }
3153
3154 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
3155 if (!js.isConstraintsSatisfied()) {
3156 js.overrideState = 0;
3157 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
3158 }
3159
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003160 queueReadyJobsForExecutionLocked();
3161 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08003162 }
3163 } catch (RemoteException e) {
3164 // can't happen
3165 }
3166 return 0;
3167 }
3168
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003169 // Shell command infrastructure: immediately timeout currently executing jobs
3170 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
3171 boolean hasJobId, int jobId) {
3172 if (DEBUG) {
3173 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
3174 }
3175
3176 synchronized (mLock) {
3177 boolean foundSome = false;
3178 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07003179 final JobServiceContext jc = mActiveServices.get(i);
3180 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08003181 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07003182 foundSome = true;
3183 pw.print("Timing out: ");
3184 js.printUniqueId(pw);
3185 pw.print(" ");
3186 pw.println(js.getServiceComponent().flattenToShortString());
3187 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003188 }
3189 if (!foundSome) {
3190 pw.println("No matching executing jobs found.");
3191 }
3192 }
3193 return 0;
3194 }
3195
Christopher Tate8c67d122017-09-29 16:54:26 -07003196 // Shell command infrastructure: cancel a scheduled job
3197 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
3198 boolean hasJobId, int jobId) {
3199 if (DEBUG) {
3200 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
3201 }
3202
3203 int pkgUid = -1;
3204 try {
3205 IPackageManager pm = AppGlobals.getPackageManager();
3206 pkgUid = pm.getPackageUid(pkgName, 0, userId);
3207 } catch (RemoteException e) { /* can't happen */ }
3208
3209 if (pkgUid < 0) {
3210 pw.println("Package " + pkgName + " not found.");
3211 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3212 }
3213
3214 if (!hasJobId) {
3215 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
3216 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
3217 pw.println("No matching jobs found.");
3218 }
3219 } else {
3220 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08003221 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07003222 pw.println("No matching job found.");
3223 }
3224 }
3225
3226 return 0;
3227 }
3228
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08003229 void setMonitorBattery(boolean enabled) {
3230 synchronized (mLock) {
3231 if (mBatteryController != null) {
3232 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
3233 }
3234 }
3235 }
3236
3237 int getBatterySeq() {
3238 synchronized (mLock) {
3239 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
3240 }
3241 }
3242
3243 boolean getBatteryCharging() {
3244 synchronized (mLock) {
3245 return mBatteryController != null
3246 ? mBatteryController.getTracker().isOnStablePower() : false;
3247 }
3248 }
3249
3250 boolean getBatteryNotLow() {
3251 synchronized (mLock) {
3252 return mBatteryController != null
3253 ? mBatteryController.getTracker().isBatteryNotLow() : false;
3254 }
3255 }
3256
Dianne Hackborn532ea262017-03-17 17:50:55 -07003257 int getStorageSeq() {
3258 synchronized (mLock) {
3259 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
3260 }
3261 }
3262
3263 boolean getStorageNotLow() {
3264 synchronized (mLock) {
3265 return mStorageController != null
3266 ? mStorageController.getTracker().isStorageNotLow() : false;
3267 }
3268 }
3269
Christopher Tatea732f012017-10-26 17:26:53 -07003270 long getCurrentHeartbeat() {
3271 synchronized (mLock) {
3272 return mHeartbeat;
3273 }
3274 }
3275
Christopher Tated1aebb32018-01-31 13:24:14 -08003276 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07003277 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
3278 try {
3279 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3280 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
3281 if (uid < 0) {
3282 pw.print("unknown("); pw.print(pkgName); pw.println(")");
3283 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3284 }
3285
3286 synchronized (mLock) {
3287 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3288 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
3289 if (js == null) {
3290 pw.print("unknown("); UserHandle.formatUid(pw, uid);
3291 pw.print("/jid"); pw.print(jobId); pw.println(")");
3292 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3293 }
3294
3295 boolean printed = false;
3296 if (mPendingJobs.contains(js)) {
3297 pw.print("pending");
3298 printed = true;
3299 }
3300 if (isCurrentlyActiveLocked(js)) {
3301 if (printed) {
3302 pw.print(" ");
3303 }
3304 printed = true;
3305 pw.println("active");
3306 }
3307 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
3308 if (printed) {
3309 pw.print(" ");
3310 }
3311 printed = true;
3312 pw.println("user-stopped");
3313 }
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003314 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
3315 if (printed) {
3316 pw.print(" ");
3317 }
3318 printed = true;
3319 pw.println("source-user-stopped");
3320 }
Dianne Hackborn6d068262017-05-16 13:14:37 -07003321 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
3322 if (printed) {
3323 pw.print(" ");
3324 }
3325 printed = true;
3326 pw.println("backing-up");
3327 }
3328 boolean componentPresent = false;
3329 try {
3330 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3331 js.getServiceComponent(),
3332 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3333 js.getUserId()) != null);
3334 } catch (RemoteException e) {
3335 }
3336 if (!componentPresent) {
3337 if (printed) {
3338 pw.print(" ");
3339 }
3340 printed = true;
3341 pw.println("no-component");
3342 }
3343 if (js.isReady()) {
3344 if (printed) {
3345 pw.print(" ");
3346 }
3347 printed = true;
3348 pw.println("ready");
3349 }
3350 if (!printed) {
3351 pw.print("waiting");
3352 }
3353 pw.println();
3354 }
3355 } catch (RemoteException e) {
3356 // can't happen
3357 }
3358 return 0;
3359 }
3360
Christopher Tated1aebb32018-01-31 13:24:14 -08003361 // Shell command infrastructure
3362 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
3363 if (numBeats < 1) {
3364 pw.println(getCurrentHeartbeat());
3365 return 0;
3366 }
3367
3368 pw.print("Advancing standby heartbeat by ");
3369 pw.println(numBeats);
3370 synchronized (mLock) {
3371 advanceHeartbeatLocked(numBeats);
3372 }
3373 return 0;
3374 }
3375
lpeter318abc92018-05-04 16:13:14 +08003376 void triggerDockState(boolean idleState) {
3377 final Intent dockIntent;
3378 if (idleState) {
3379 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3380 } else {
3381 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3382 }
3383 dockIntent.setPackage("android");
3384 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3385 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3386 }
3387
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003388 static void dumpHelp(PrintWriter pw) {
3389 pw.println("Job Scheduler (jobscheduler) dump options:");
3390 pw.println(" [-h] [package] ...");
3391 pw.println(" -h: print this help");
3392 pw.println(" [package] is an optional package name to limit the output to.");
3393 }
3394
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003395 /** Sort jobs by caller UID, then by Job ID. */
3396 private static void sortJobs(List<JobStatus> jobs) {
3397 Collections.sort(jobs, new Comparator<JobStatus>() {
3398 @Override
3399 public int compare(JobStatus o1, JobStatus o2) {
3400 int uid1 = o1.getUid();
3401 int uid2 = o2.getUid();
3402 int id1 = o1.getJobId();
3403 int id2 = o2.getJobId();
3404 if (uid1 != uid2) {
3405 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003406 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003407 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003408 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003409 });
3410 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003411
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003412 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003413 final int filterUidFinal = UserHandle.getAppId(filterUid);
Makoto Onukif731c422019-01-22 13:23:31 -08003414 final long now = sSystemClock.millis();
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003415 final long nowElapsed = sElapsedRealtimeClock.millis();
3416 final long nowUptime = sUptimeMillisClock.millis();
Makoto Onukif731c422019-01-22 13:23:31 -08003417
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003418 final Predicate<JobStatus> predicate = (js) -> {
3419 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3420 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3421 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003422 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003423 mConstants.dump(pw);
3424 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003425
3426 pw.println(" Heartbeat:");
3427 pw.print(" Current: "); pw.println(mHeartbeat);
3428 pw.println(" Next");
3429 pw.print(" ACTIVE: "); pw.println(mNextBucketHeartbeat[0]);
3430 pw.print(" WORKING: "); pw.println(mNextBucketHeartbeat[1]);
3431 pw.print(" FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3432 pw.print(" RARE: "); pw.println(mNextBucketHeartbeat[3]);
3433 pw.print(" Last heartbeat: ");
3434 TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3435 pw.println();
3436 pw.print(" Next heartbeat: ");
3437 TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3438 nowElapsed, pw);
3439 pw.println();
Makoto Onuki0525b982018-05-02 14:46:59 -07003440 pw.print(" In parole?: ");
3441 pw.print(mInParole);
3442 pw.println();
Wei Wang8c0c3c12018-11-14 14:56:52 -08003443 pw.print(" In thermal throttling?: ");
3444 pw.print(mThermalConstraint);
3445 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003446 pw.println();
3447
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003448 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003449 pw.print("Registered ");
3450 pw.print(mJobs.size());
3451 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003452 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003453 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003454 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003455 for (JobStatus job : jobs) {
3456 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3457 pw.println(job.toShortStringExceptUniqueId());
3458
3459 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003460 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003461 continue;
3462 }
3463
Dianne Hackborn6d068262017-05-16 13:14:37 -07003464 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003465 pw.print(" Last run heartbeat: ");
3466 pw.print(heartbeatWhenJobsLastRun(job));
3467 pw.println();
3468
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003469 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003470 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003471 pw.print(" (job=");
3472 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003473 pw.print(" user=");
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003474 pw.print(areUsersStartedLocked(job));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003475 pw.print(" !pending=");
3476 pw.print(!mPendingJobs.contains(job));
3477 pw.print(" !active=");
3478 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003479 pw.print(" !backingup=");
3480 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003481 pw.print(" comp=");
3482 boolean componentPresent = false;
3483 try {
3484 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3485 job.getServiceComponent(),
3486 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3487 job.getUserId()) != null);
3488 } catch (RemoteException e) {
3489 }
3490 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003491 pw.println(")");
3492 }
Christopher Tate7060b042014-06-09 19:50:00 -07003493 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003494 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003495 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003496 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003497 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003498 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3499 pw.increaseIndent();
3500 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3501 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003502 }
3503 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003504 pw.println("Uid priority overrides:");
3505 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003506 int uid = mUidPriorityOverride.keyAt(i);
3507 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3508 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3509 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3510 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003511 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003512 if (mBackingUpUids.size() > 0) {
3513 pw.println();
3514 pw.println("Backing up uids:");
3515 boolean first = true;
3516 for (int i = 0; i < mBackingUpUids.size(); i++) {
3517 int uid = mBackingUpUids.keyAt(i);
3518 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3519 if (first) {
3520 pw.print(" ");
3521 first = false;
3522 } else {
3523 pw.print(", ");
3524 }
3525 pw.print(UserHandle.formatUid(uid));
3526 }
3527 }
3528 pw.println();
3529 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003530 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003531 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003532 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003533 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3534 pw.println();
3535 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003536 pw.println("Pending queue:");
3537 for (int i=0; i<mPendingJobs.size(); i++) {
3538 JobStatus job = mPendingJobs.get(i);
3539 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3540 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003541 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003542 int priority = evaluateJobPriorityLocked(job);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003543 pw.print(" Evaluated priority: ");
3544 pw.println(JobInfo.getPriorityString(priority));
3545
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003546 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003547 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003548 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003549 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003550 }
Christopher Tate7060b042014-06-09 19:50:00 -07003551 pw.println();
3552 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003553 for (int i=0; i<mActiveServices.size(); i++) {
3554 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003555 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003556 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003557 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003558 if (jsc.mStoppedReason != null) {
3559 pw.print("inactive since ");
3560 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3561 pw.print(", stopped because: ");
3562 pw.println(jsc.mStoppedReason);
3563 } else {
3564 pw.println("inactive");
3565 }
Christopher Tate7060b042014-06-09 19:50:00 -07003566 continue;
3567 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003568 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003569 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003570 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003571 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003572 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003573 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003574 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003575 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003576 pw.print(" Evaluated priority: ");
3577 pw.println(JobInfo.getPriorityString(priority));
3578
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003579 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003580 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003581 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003582 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3583 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003584 }
3585 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003586 if (filterUid == -1) {
3587 pw.println();
3588 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3589 pw.print("mReportedActive="); pw.println(mReportedActive);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003590 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003591 pw.println();
Makoto Onukic279f2b2019-01-09 13:11:37 -08003592
Makoto Onukif731c422019-01-22 13:23:31 -08003593 mConcurrencyManager.dumpLocked(pw, now, nowElapsed);
Makoto Onukic279f2b2019-01-09 13:11:37 -08003594
3595 pw.println();
Makoto Onukie7b02982017-08-24 14:23:36 -07003596 pw.print("PersistStats: ");
3597 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003598 }
3599 pw.println();
3600 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003601
3602 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3603 ProtoOutputStream proto = new ProtoOutputStream(fd);
3604 final int filterUidFinal = UserHandle.getAppId(filterUid);
Makoto Onukif731c422019-01-22 13:23:31 -08003605 final long now = sSystemClock.millis();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003606 final long nowElapsed = sElapsedRealtimeClock.millis();
3607 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003608 final Predicate<JobStatus> predicate = (js) -> {
3609 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3610 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3611 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003612
3613 synchronized (mLock) {
3614 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003615 proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3616 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3617 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3618 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3619 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3620 proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3621 mLastHeartbeatTime - nowUptime);
3622 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3623 mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
Makoto Onuki0525b982018-05-02 14:46:59 -07003624 proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
Wei Wang8c0c3c12018-11-14 14:56:52 -08003625 proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003626
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003627 for (int u : mStartedUsers) {
3628 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3629 }
3630 if (mJobs.size() > 0) {
3631 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3632 sortJobs(jobs);
3633 for (JobStatus job : jobs) {
3634 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3635 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3636
3637 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003638 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003639 continue;
3640 }
3641
3642 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3643
3644 // isReadyToBeExecuted
3645 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3646 job.isReady());
3647 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003648 areUsersStartedLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003649 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3650 mPendingJobs.contains(job));
3651 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3652 isCurrentlyActiveLocked(job));
3653 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3654 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3655 boolean componentPresent = false;
3656 try {
3657 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3658 job.getServiceComponent(),
3659 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3660 job.getUserId()) != null);
3661 } catch (RemoteException e) {
3662 }
3663 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3664 componentPresent);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003665 proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003666
3667 proto.end(rjToken);
3668 }
3669 }
3670 for (StateController controller : mControllers) {
3671 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003672 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003673 }
3674 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3675 int uid = mUidPriorityOverride.keyAt(i);
3676 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3677 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3678 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3679 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3680 mUidPriorityOverride.valueAt(i));
3681 proto.end(pToken);
3682 }
3683 }
3684 for (int i = 0; i < mBackingUpUids.size(); i++) {
3685 int uid = mBackingUpUids.keyAt(i);
3686 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3687 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3688 }
3689 }
3690
3691 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3692 filterUidFinal);
3693 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3694 filterUidFinal);
3695
3696 for (JobStatus job : mPendingJobs) {
3697 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3698
3699 job.writeToShortProto(proto, PendingJob.INFO);
3700 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003701 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003702 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3703
3704 proto.end(pjToken);
3705 }
3706 for (JobServiceContext jsc : mActiveServices) {
3707 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3708 final JobStatus job = jsc.getRunningJobLocked();
3709
3710 if (job == null) {
3711 final long ijToken = proto.start(ActiveJob.INACTIVE);
3712
3713 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3714 nowElapsed - jsc.mStoppedTime);
3715 if (jsc.mStoppedReason != null) {
3716 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3717 jsc.mStoppedReason);
3718 }
3719
3720 proto.end(ijToken);
3721 } else {
3722 final long rjToken = proto.start(ActiveJob.RUNNING);
3723
3724 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3725
3726 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3727 nowElapsed - jsc.getExecutionStartTimeElapsed());
3728 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3729 jsc.getTimeoutElapsed() - nowElapsed);
3730
3731 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3732
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003733 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
3734 evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003735
3736 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3737 nowUptime - job.madeActive);
3738 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3739 job.madeActive - job.madePending);
3740
3741 proto.end(rjToken);
3742 }
3743 proto.end(ajToken);
3744 }
3745 if (filterUid == -1) {
3746 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3747 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003748 }
Makoto Onukif731c422019-01-22 13:23:31 -08003749 mConcurrencyManager.dumpProtoLocked(proto,
3750 JobSchedulerServiceDumpProto.CONCURRENCY_MANAGER, now, nowElapsed);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003751 }
3752
3753 proto.flush();
3754 }
Christopher Tate7060b042014-06-09 19:50:00 -07003755}