blob: 9ca6cf651f2f86471813f9431cdedef55270c27e [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 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800227 * Current limit on the number of concurrent JobServiceContext entries we want to
228 * keep actively running a job.
229 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700230 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800231
232 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800233 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800234 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800235 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
236
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700237 /**
238 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
239 */
240 final SparseIntArray mBackingUpUids = new SparseIntArray();
241
Christopher Tatea732f012017-10-26 17:26:53 -0700242 /**
243 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
244 * next become runnable. Index into this array is by normalized bucket:
245 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
246 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
247 * and NEVER apps don't get them at all.
248 */
249 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
250 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800251 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
252
Christopher Tate325768c2018-03-07 16:07:56 -0800253 /**
254 * Named indices into the STANDBY_BEATS array, for clarity in referring to
255 * specific buckets' bookkeeping.
256 */
Kweku Adams4836f9d2018-11-12 17:04:17 -0800257 public static final int ACTIVE_INDEX = 0;
258 public static final int WORKING_INDEX = 1;
259 public static final int FREQUENT_INDEX = 2;
260 public static final int RARE_INDEX = 3;
261 public static final int NEVER_INDEX = 4;
Christopher Tate325768c2018-03-07 16:07:56 -0800262
263 /**
264 * Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
265 * rather than rely on Usage Stats' timestamps, because heartbeat time can be
266 * manipulated for testing purposes and we need job runnability to track that rather
267 * than real time.
268 *
269 * Outer SparseArray slices by user handle; inner map of package name to heartbeat
270 * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
271 * and it will be accessed in a known-hot code path.
272 */
273 final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
274
Christopher Tated1aebb32018-01-31 13:24:14 -0800275 static final String HEARTBEAT_TAG = "*job.heartbeat*";
276 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700277
Dianne Hackborn970510b2016-02-24 16:56:42 -0800278 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
279
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700280 private class ConstantsObserver extends ContentObserver {
281 private ContentResolver mResolver;
282
283 public ConstantsObserver(Handler handler) {
284 super(handler);
285 }
286
287 public void start(ContentResolver resolver) {
288 mResolver = resolver;
289 mResolver.registerContentObserver(Settings.Global.getUriFor(
290 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
291 updateConstants();
292 }
293
294 @Override
295 public void onChange(boolean selfChange, Uri uri) {
296 updateConstants();
297 }
298
299 private void updateConstants() {
300 synchronized (mLock) {
301 try {
302 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
303 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Kweku Adams4836f9d2018-11-12 17:04:17 -0800304 for (int controller = 0; controller < mControllers.size(); controller++) {
305 final StateController sc = mControllers.get(controller);
306 sc.onConstantsUpdatedLocked();
307 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700308 } catch (IllegalArgumentException e) {
309 // Failed to parse the settings string, log this and move on
310 // with defaults.
311 Slog.e(TAG, "Bad jobscheduler settings", e);
312 }
313 }
314
Kweku Adams4836f9d2018-11-12 17:04:17 -0800315 if (mConstants.USE_HEARTBEATS) {
316 // Reset the heartbeat alarm based on the new heartbeat duration
317 setNextHeartbeatAlarm();
318 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700319 }
320 }
321
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800322 /**
Wei Wang8c0c3c12018-11-14 14:56:52 -0800323 * Thermal event received from Thermal Service
324 */
325 private final class ThermalStatusListener extends IThermalStatusListener.Stub {
326 @Override public void onStatusChange(int status) {
327 // Throttle for Temperature.THROTTLING_SEVERE and above
328 synchronized (mLock) {
329 mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
330 }
331 onControllerStateChanged();
332 }
333 }
334
Makoto Onuki66a51442018-12-20 14:23:50 -0800335 private static class MaxJobCounts {
336 private final KeyValueListParser.IntValue mTotal;
337 private final KeyValueListParser.IntValue mBg;
338
339 private MaxJobCounts(int totalDefault, String totalKey, int bgDefault, String bgKey) {
340 mTotal = new KeyValueListParser.IntValue(totalKey, totalDefault);
341 mBg = new KeyValueListParser.IntValue(bgKey, bgDefault);
342 }
343
344 public void parse(KeyValueListParser parser) {
345 mTotal.parse(parser);
346 mBg.parse(parser);
347
348 if (mBg.getValue() > mTotal.getValue()) {
349 mBg.setValue(mTotal.getValue());
350 }
351
352 }
353
354 public int getTotalMax() {
355 return mTotal.getValue();
356 }
357
358 public int getBgMax() {
359 return mBg.getValue();
360 }
361
362 public void dump(PrintWriter pw, String prefix) {
363 mTotal.dump(pw, prefix);
364 mBg.dump(pw, prefix);
365 }
366
367 public void dumpProto(ProtoOutputStream proto, long tagTotal, long tagBg) {
368 mTotal.dumpProto(proto, tagTotal);
369 mBg.dumpProto(proto, tagBg);
370 }
371 }
372
Wei Wang8c0c3c12018-11-14 14:56:52 -0800373 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700374 * All times are in milliseconds. These constants are kept synchronized with the system
375 * global Settings. Any access to this class or its fields should be done while
376 * holding the JobSchedulerService.mLock lock.
377 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700378 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700379 // Key names stored in the settings value.
380 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
381 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800382 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700383 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700384 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
385 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
386 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
387 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
388 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
389 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
390 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
391 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
392 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
393 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700394 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
395 = "max_standard_reschedule_count";
396 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
397 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
398 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700399 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
400 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
401 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
402 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700403 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
404 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Kweku Adams4836f9d2018-11-12 17:04:17 -0800405 private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800406 private static final String KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
407 "tc_skip_not_ready_jobs";
Kweku Adams4836f9d2018-11-12 17:04:17 -0800408 private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
409 "qc_allowed_time_per_period_ms";
410 private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
411 "qc_in_quota_buffer_ms";
412 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
413 "qc_window_size_active_ms";
414 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
415 "qc_window_size_working_ms";
416 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
417 "qc_window_size_frequent_ms";
418 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
419 "qc_window_size_rare_ms";
Kweku Adams045fb5722018-12-11 14:29:10 -0800420 private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
421 "qc_max_execution_time_ms";
Kweku Adams288e73b2019-01-17 13:53:24 -0800422 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
423 "qc_max_job_count_active";
424 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
425 "qc_max_job_count_working";
426 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
427 "qc_max_job_count_frequent";
428 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
429 "qc_max_job_count_rare";
430 private static final String KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
431 "qc_max_count_per_allowed_time";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700432
433 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
434 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800435 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700436 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700437 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
438 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
439 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
440 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
441 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
442 private static final int DEFAULT_FG_JOB_COUNT = 4;
443 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
444 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700445 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700446 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700447 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
448 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
449 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
450 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700451 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000452 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
453 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700454 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700455 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
456 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800457 private static final boolean DEFAULT_USE_HEARTBEATS = true;
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800458 private static final boolean DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS = false;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800459 private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
460 10 * 60 * 1000L; // 10 minutes
461 private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
462 30 * 1000L; // 30 seconds
463 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
464 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
465 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
466 2 * 60 * 60 * 1000L; // 2 hours
467 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
468 8 * 60 * 60 * 1000L; // 8 hours
469 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
470 24 * 60 * 60 * 1000L; // 24 hours
Kweku Adams045fb5722018-12-11 14:29:10 -0800471 private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
472 4 * 60 * 60 * 1000L; // 4 hours
Kweku Adams288e73b2019-01-17 13:53:24 -0800473 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
474 200; // 1200/hr
475 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
476 1200; // 600/hr
477 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
478 1800; // 225/hr
479 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
480 2400; // 100/hr
481 private static final int DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = 20;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700482
483 /**
484 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
485 * early.
486 */
487 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
488 /**
489 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
490 * things early.
491 */
492 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
493 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800494 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
495 * schedule things early.
496 */
497 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
498 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700499 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
500 * schedule things early.
501 */
502 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
503 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700504 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
505 * things early. 1 == Run connectivity jobs as soon as ready.
506 */
507 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
508 /**
509 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
510 * schedule things early.
511 */
512 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
513 /**
514 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
515 * running some work early. This (and thus the other min counts) is now set to 1, to
516 * prevent any batching at this level. Since we now do batching through doze, that is
517 * a much better mechanism.
518 */
519 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
520 /**
521 * This is the job execution factor that is considered to be heavy use of the system.
522 */
523 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
524 /**
525 * This is the job execution factor that is considered to be moderate use of the system.
526 */
527 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
528 /**
529 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
530 */
531 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
532 /**
533 * The maximum number of background jobs we allow when the system is in a normal
534 * memory state.
535 */
536 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
537 /**
538 * The maximum number of background jobs we allow when the system is in a moderate
539 * memory state.
540 */
541 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
542 /**
543 * The maximum number of background jobs we allow when the system is in a low
544 * memory state.
545 */
546 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
547 /**
548 * The maximum number of background jobs we allow when the system is in a critical
549 * memory state.
550 */
551 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Makoto Onuki66a51442018-12-20 14:23:50 -0800552
553 // Max job counts for screen on / off, for each memory trim level.
554 // TODO Remove the old configs such as FG_JOB_COUNT and BG_*_COUNT, once the code switches
555 // to the below configs.
556
557 final MaxJobCounts MAX_JOB_COUNTS_ON_NORMAL = new MaxJobCounts(
558 4, "max_job_total_on_normal",
559 2, "max_job_bg_on_normal");
560
561 final MaxJobCounts MAX_JOB_COUNTS_ON_MODERATE = new MaxJobCounts(
562 4, "max_job_total_on_moderate",
563 1, "max_job_bg_on_moderate");
564
565 final MaxJobCounts MAX_JOB_COUNTS_ON_LOW = new MaxJobCounts(
566 4, "max_job_total_on_low",
567 1, "max_job_bg_on_low");
568
569 final MaxJobCounts MAX_JOB_COUNTS_ON_CRITICAL = new MaxJobCounts(
570 2, "max_job_total_on_critical",
571 1, "max_job_bg_on_critical");
572
573 final MaxJobCounts MAX_JOB_COUNTS_OFF_NORMAL = new MaxJobCounts(
574 8, "max_job_total_off_normal",
575 4, "max_job_bg_off_normal");
576
577 final MaxJobCounts MAX_JOB_COUNTS_OFF_MODERATE = new MaxJobCounts(
578 6, "max_job_total_off_moderate",
579 4, "max_job_bg_off_moderate");
580
581 final MaxJobCounts MAX_JOB_COUNTS_OFF_LOW = new MaxJobCounts(
582 4, "max_job_total_off_low",
583 1, "max_job_bg_off_low");
584
585 final MaxJobCounts MAX_JOB_COUNTS_OFF_CRITICAL = new MaxJobCounts(
586 2, "max_job_total_off_critical",
587 1, "max_job_bg_off_critical");
588
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700589 /**
590 * The maximum number of times we allow a job to have itself rescheduled before
591 * giving up on it, for standard jobs.
592 */
593 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
594 /**
595 * The maximum number of times we allow a job to have itself rescheduled before
596 * giving up on it, for jobs that are executing work.
597 */
598 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
599 /**
600 * The minimum backoff time to allow for linear backoff.
601 */
602 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
603 /**
604 * The minimum backoff time to allow for exponential backoff.
605 */
606 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700607 /**
608 * How often we recalculate runnability based on apps' standby bucket assignment.
609 * This should be prime relative to common time interval lengths such as a quarter-
610 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
611 */
612 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700613 /**
614 * Mapping: standby bucket -> number of heartbeats between each sweep of that
615 * bucket's jobs.
616 *
617 * Bucket assignments as recorded in the JobStatus objects are normalized to be
618 * indices into this array, rather than the raw constants used
619 * by AppIdleHistory.
620 */
621 final int[] STANDBY_BEATS = {
622 0,
623 DEFAULT_STANDBY_WORKING_BEATS,
624 DEFAULT_STANDBY_FREQUENT_BEATS,
625 DEFAULT_STANDBY_RARE_BEATS
626 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700627 /**
628 * The fraction of a job's running window that must pass before we
629 * consider running it when the network is congested.
630 */
631 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
632 /**
633 * The fraction of a prefetch job's running window that must pass before
634 * we consider matching it against a metered network.
635 */
636 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800637 /**
638 * Whether to use heartbeats or rolling window for quota management. True will use
639 * heartbeats, false will use a rolling window.
640 */
641 public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
642
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800643 /**
644 * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
645 * ready now.
646 */
647 public boolean TIME_CONTROLLER_SKIP_NOT_READY_JOBS =
648 DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS;
649
Kweku Adams4836f9d2018-11-12 17:04:17 -0800650 /** How much time each app will have to run jobs within their standby bucket window. */
651 public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
652 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
653
654 /**
655 * How much time the package should have before transitioning from out-of-quota to in-quota.
656 * This should not affect processing if the package is already in-quota.
657 */
658 public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
659 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
660
661 /**
662 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800663 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800664 * WINDOW_SIZE_MS.
665 */
666 public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
667 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
668
669 /**
670 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800671 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800672 * WINDOW_SIZE_MS.
673 */
674 public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
675 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
676
677 /**
678 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800679 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800680 * WINDOW_SIZE_MS.
681 */
682 public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
683 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
684
685 /**
686 * The quota window size of the particular standby bucket. Apps in this standby bucket are
Makoto Onuki66a51442018-12-20 14:23:50 -0800687 * expected to run only {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
Kweku Adams4836f9d2018-11-12 17:04:17 -0800688 * WINDOW_SIZE_MS.
689 */
690 public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
691 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700692
Kweku Adams045fb5722018-12-11 14:29:10 -0800693 /**
694 * The maximum amount of time an app can have its jobs running within a 24 hour window.
695 */
696 public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
697 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
698
Kweku Adams288e73b2019-01-17 13:53:24 -0800699 /**
700 * The maximum number of jobs an app can run within this particular standby bucket's
701 * window size.
702 */
703 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE =
704 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE;
705
706 /**
707 * The maximum number of jobs an app can run within this particular standby bucket's
708 * window size.
709 */
710 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING =
711 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING;
712
713 /**
714 * The maximum number of jobs an app can run within this particular standby bucket's
715 * window size.
716 */
717 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT =
718 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT;
719
720 /**
721 * The maximum number of jobs an app can run within this particular standby bucket's
722 * window size.
723 */
724 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE =
725 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE;
726
727 /**
728 * The maximum number of jobs that can run within the past
729 * {@link #QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS}.
730 */
731 public int QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME =
732 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME;
733
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700734 private final KeyValueListParser mParser = new KeyValueListParser(',');
735
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700736 void updateConstantsLocked(String value) {
737 try {
738 mParser.setString(value);
739 } catch (Exception e) {
740 // Failed to parse the settings string, log this and move on
741 // with defaults.
742 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700743 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800744
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700745 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
746 DEFAULT_MIN_IDLE_COUNT);
747 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
748 DEFAULT_MIN_CHARGING_COUNT);
749 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
750 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
751 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
752 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
753 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
754 DEFAULT_MIN_CONNECTIVITY_COUNT);
755 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
756 DEFAULT_MIN_CONTENT_COUNT);
757 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
758 DEFAULT_MIN_READY_JOBS_COUNT);
759 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
760 DEFAULT_HEAVY_USE_FACTOR);
761 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
762 DEFAULT_MODERATE_USE_FACTOR);
763 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
764 DEFAULT_FG_JOB_COUNT);
765 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
766 DEFAULT_BG_NORMAL_JOB_COUNT);
767 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
768 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
769 }
770 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
771 DEFAULT_BG_MODERATE_JOB_COUNT);
772 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
773 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
774 }
775 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
776 DEFAULT_BG_LOW_JOB_COUNT);
777 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
778 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
779 }
780 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
781 DEFAULT_BG_CRITICAL_JOB_COUNT);
782 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
783 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
784 }
Makoto Onuki66a51442018-12-20 14:23:50 -0800785
786 MAX_JOB_COUNTS_ON_NORMAL.parse(mParser);
787 MAX_JOB_COUNTS_ON_MODERATE.parse(mParser);
788 MAX_JOB_COUNTS_ON_LOW.parse(mParser);
789 MAX_JOB_COUNTS_ON_CRITICAL.parse(mParser);
790
791 MAX_JOB_COUNTS_OFF_NORMAL.parse(mParser);
792 MAX_JOB_COUNTS_OFF_MODERATE.parse(mParser);
793 MAX_JOB_COUNTS_OFF_LOW.parse(mParser);
794 MAX_JOB_COUNTS_OFF_CRITICAL.parse(mParser);
795
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700796 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
797 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
798 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
799 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
800 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
801 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
802 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
803 DEFAULT_MIN_EXP_BACKOFF_TIME);
804 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
805 DEFAULT_STANDBY_HEARTBEAT_TIME);
Christopher Tate325768c2018-03-07 16:07:56 -0800806 STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700807 DEFAULT_STANDBY_WORKING_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800808 STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700809 DEFAULT_STANDBY_FREQUENT_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800810 STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700811 DEFAULT_STANDBY_RARE_BEATS);
812 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
813 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
814 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
815 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800816 USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800817 TIME_CONTROLLER_SKIP_NOT_READY_JOBS = mParser.getBoolean(
818 KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
819 DEFAULT_TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800820 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
821 KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
822 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
823 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
824 KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
825 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
826 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
827 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
828 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
829 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
830 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
831 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
832 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
833 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
834 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
835 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
836 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
837 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800838 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
839 KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
840 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Kweku Adams288e73b2019-01-17 13:53:24 -0800841 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE = mParser.getInt(
842 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
843 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
844 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING = mParser.getInt(
845 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
846 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
847 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT = mParser.getInt(
848 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
849 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
850 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE = mParser.getInt(
851 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
852 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
853 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME = mParser.getInt(
854 KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
855 DEFAULT_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700856 }
857
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700858 void dump(IndentingPrintWriter pw) {
859 pw.println("Settings:");
860 pw.increaseIndent();
861 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
862 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
863 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
864 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
865 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
866 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
867 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
868 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
869 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
870 pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
871 pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
872 pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
873 pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
874 pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
Makoto Onuki66a51442018-12-20 14:23:50 -0800875
876 MAX_JOB_COUNTS_ON_NORMAL.dump(pw, "");
877 MAX_JOB_COUNTS_ON_MODERATE.dump(pw, "");
878 MAX_JOB_COUNTS_ON_LOW.dump(pw, "");
879 MAX_JOB_COUNTS_ON_CRITICAL.dump(pw, "");
880
881 MAX_JOB_COUNTS_OFF_NORMAL.dump(pw, "");
882 MAX_JOB_COUNTS_OFF_MODERATE.dump(pw, "");
883 MAX_JOB_COUNTS_OFF_LOW.dump(pw, "");
884 MAX_JOB_COUNTS_OFF_CRITICAL.dump(pw, "");
885
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700886 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
887 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
888 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
889 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
890 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
891 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700892 pw.print(STANDBY_BEATS[0]);
893 for (int i = 1; i < STANDBY_BEATS.length; i++) {
894 pw.print(", ");
895 pw.print(STANDBY_BEATS[i]);
896 }
897 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700898 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
899 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
Kweku Adams4836f9d2018-11-12 17:04:17 -0800900 pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800901 pw.printPair(KEY_TIME_CONTROLLER_SKIP_NOT_READY_JOBS,
902 TIME_CONTROLLER_SKIP_NOT_READY_JOBS).println();
Kweku Adams4836f9d2018-11-12 17:04:17 -0800903 pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
904 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
905 pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
906 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
907 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
908 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
909 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
910 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
911 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
912 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
913 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
914 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
Kweku Adams045fb5722018-12-11 14:29:10 -0800915 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
916 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
Kweku Adams288e73b2019-01-17 13:53:24 -0800917 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE,
918 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE).println();
919 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING,
920 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING).println();
921 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT,
922 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT).println();
923 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE,
924 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE).println();
925 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME,
926 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME).println();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700927 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700928 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800929
930 void dump(ProtoOutputStream proto, long fieldId) {
931 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800932 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
933 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
934 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
935 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
936 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
937 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
938 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
939 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
940 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
941 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
942 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
943 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
944 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
945 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
Makoto Onuki66a51442018-12-20 14:23:50 -0800946
947 // TODO Dump max job counts.
948
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800949 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
950 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
951 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
952 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
953 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800954 for (int period : STANDBY_BEATS) {
955 proto.write(ConstantsProto.STANDBY_BEATS, period);
956 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700957 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
958 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800959 proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
960
Kweku Adamsbffea5a2018-12-13 22:13:28 -0800961 final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
962 proto.write(ConstantsProto.TimeController.SKIP_NOT_READY_JOBS,
963 TIME_CONTROLLER_SKIP_NOT_READY_JOBS);
964 proto.end(tcToken);
965
Kweku Adams4836f9d2018-11-12 17:04:17 -0800966 final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
967 proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
968 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
969 proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
970 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
971 proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
972 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
973 proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
974 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
975 proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
976 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
977 proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
978 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800979 proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
980 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Kweku Adams288e73b2019-01-17 13:53:24 -0800981 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_ACTIVE,
982 QUOTA_CONTROLLER_MAX_JOB_COUNT_ACTIVE);
983 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_WORKING,
984 QUOTA_CONTROLLER_MAX_JOB_COUNT_WORKING);
985 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_FREQUENT,
986 QUOTA_CONTROLLER_MAX_JOB_COUNT_FREQUENT);
987 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_RARE,
988 QUOTA_CONTROLLER_MAX_JOB_COUNT_RARE);
989 proto.write(ConstantsProto.QuotaController.MAX_JOB_COUNT_PER_ALLOWED_TIME,
990 QUOTA_CONTROLLER_MAX_JOB_COUNT_PER_ALLOWED_TIME);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800991 proto.end(qcToken);
992
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800993 proto.end(token);
994 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700995 }
996
997 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700998 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700999
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001000 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
1001 if (o1.enqueueTime < o2.enqueueTime) {
1002 return -1;
1003 }
1004 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
1005 };
1006
1007 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
1008 int where = Collections.binarySearch(array, newItem, comparator);
1009 if (where < 0) {
1010 where = ~where;
1011 }
1012 array.add(where, newItem);
1013 }
1014
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001015 /**
Christopher Tate7060b042014-06-09 19:50:00 -07001016 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
1017 * still clean up. On reinstall the package will have a new uid.
1018 */
1019 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
1020 @Override
1021 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -07001022 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -07001023 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -07001024 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -07001025 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001026 final String pkgName = getPackageName(intent);
1027 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
1028
Christopher Tateee7805b2016-07-15 16:56:56 -07001029 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -07001030 // Purge the app's jobs if the whole package was just disabled. When this is
1031 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -07001032 if (pkgName != null && pkgUid != -1) {
1033 final String[] changedComponents = intent.getStringArrayExtra(
1034 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
1035 if (changedComponents != null) {
1036 for (String component : changedComponents) {
1037 if (component.equals(pkgName)) {
1038 if (DEBUG) {
1039 Slog.d(TAG, "Package state change: " + pkgName);
1040 }
1041 try {
1042 final int userId = UserHandle.getUserId(pkgUid);
1043 IPackageManager pm = AppGlobals.getPackageManager();
1044 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
1045 if (state == COMPONENT_ENABLED_STATE_DISABLED
1046 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
1047 if (DEBUG) {
1048 Slog.d(TAG, "Removing jobs for package " + pkgName
1049 + " in user " + userId);
1050 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001051 cancelJobsForPackageAndUid(pkgName, pkgUid,
1052 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -07001053 }
Christopher Tate652c5ad2016-10-05 14:45:46 -07001054 } catch (RemoteException|IllegalArgumentException e) {
1055 /*
1056 * IllegalArgumentException means that the package doesn't exist.
1057 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
1058 * behind outright uninstall, so by the time we try to act it's gone.
1059 * We don't need to act on this PACKAGE_CHANGED when this happens;
1060 * we'll get a PACKAGE_REMOVED later and clean up then.
1061 *
1062 * RemoteException can't actually happen; the package manager is
1063 * running in this same process.
1064 */
1065 }
Christopher Tateb5c07882016-05-26 17:11:09 -07001066 break;
1067 }
1068 }
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001069 if (DEBUG) {
1070 Slog.d(TAG, "Something in " + pkgName
1071 + " changed. Reevaluating controller states.");
1072 }
1073 synchronized (mLock) {
1074 for (int c = mControllers.size() - 1; c >= 0; --c) {
1075 mControllers.get(c).reevaluateStateLocked(pkgUid);
1076 }
1077 }
Christopher Tateb5c07882016-05-26 17:11:09 -07001078 }
1079 } else {
1080 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
1081 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001082 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -07001083 // If this is an outright uninstall rather than the first half of an
1084 // app update sequence, cancel the jobs associated with the app.
1085 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
1086 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
1087 if (DEBUG) {
1088 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
1089 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001090 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -08001091 synchronized (mLock) {
1092 for (int c = 0; c < mControllers.size(); ++c) {
1093 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
1094 }
1095 }
Christopher Tate7060b042014-06-09 19:50:00 -07001096 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001097 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -07001098 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
1099 if (DEBUG) {
1100 Slog.d(TAG, "Removing jobs for user: " + userId);
1101 }
1102 cancelJobsForUser(userId);
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -08001103 synchronized (mLock) {
1104 for (int c = 0; c < mControllers.size(); ++c) {
1105 mControllers.get(c).onUserRemovedLocked(userId);
1106 }
1107 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001108 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
1109 // Has this package scheduled any jobs, such that we will take action
1110 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -07001111 if (pkgUid != -1) {
1112 List<JobStatus> jobsForUid;
1113 synchronized (mLock) {
1114 jobsForUid = mJobs.getJobsByUid(pkgUid);
1115 }
1116 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1117 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
1118 if (DEBUG) {
1119 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
1120 + pkgUid + " has jobs");
1121 }
1122 setResultCode(Activity.RESULT_OK);
1123 break;
1124 }
1125 }
1126 }
1127 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
1128 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -07001129 if (pkgUid != -1) {
1130 if (DEBUG) {
1131 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
1132 }
Makoto Onukie7b96182017-08-30 14:53:16 -07001133 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -07001134 }
Christopher Tate7060b042014-06-09 19:50:00 -07001135 }
1136 }
1137 };
1138
Christopher Tateb5c07882016-05-26 17:11:09 -07001139 private String getPackageName(Intent intent) {
1140 Uri uri = intent.getData();
1141 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
1142 return pkg;
1143 }
1144
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001145 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001146 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001147 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001148 }
1149
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001150 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001151 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001152 }
1153
1154 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001155 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001156 }
1157
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001158 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001159 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001160 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -07001161
1162 @Override public void onUidCachedChanged(int uid, boolean cached) {
1163 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001164 };
1165
Jeff Sharkey4d89e422018-03-29 18:22:41 -06001166 public Context getTestableContext() {
1167 return getContext();
1168 }
1169
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001170 public Object getLock() {
1171 return mLock;
1172 }
1173
Dianne Hackborn8db0fc12016-04-12 13:48:25 -07001174 public JobStore getJobStore() {
1175 return mJobs;
1176 }
1177
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001178 public Constants getConstants() {
1179 return mConstants;
1180 }
1181
Kweku Adamsbffea5a2018-12-13 22:13:28 -08001182 public boolean isChainedAttributionEnabled() {
1183 return WorkSource.isChainedBatteryAttributionEnabled(getContext());
1184 }
1185
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001186 @Override
1187 public void onStartUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -08001188 synchronized (mLock) {
1189 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
1190 }
Jeff Sharkey822cbd12016-02-25 11:09:55 -07001191 // Let's kick any outstanding jobs for this user.
1192 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1193 }
1194
1195 @Override
1196 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001197 // Let's kick any outstanding jobs for this user.
1198 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1199 }
1200
1201 @Override
1202 public void onStopUser(int userHandle) {
Kweku Adams8bd5edc2018-12-07 18:33:39 -08001203 synchronized (mLock) {
1204 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
1205 }
Matthew Williams9ae3dbe2014-08-21 13:47:47 -07001206 }
1207
Makoto Onuki15407842018-01-19 14:23:11 -08001208 /**
Makoto Onukie4918212018-02-06 11:30:15 -08001209 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -08001210 */
Makoto Onukie4918212018-02-06 11:30:15 -08001211 private boolean isUidActive(int uid) {
1212 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -08001213 }
1214
Makoto Onukie4918212018-02-06 11:30:15 -08001215 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -08001216
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001217 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
1218 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001219 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001220 if (ActivityManager.getService().isAppStartModeDisabled(uId,
1221 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001222 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1223 + " -- package not allowed to start");
1224 return JobScheduler.RESULT_FAILURE;
1225 }
1226 } catch (RemoteException e) {
1227 }
Christopher Tatea732f012017-10-26 17:26:53 -07001228
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001229 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001230 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1231
1232 if (work != null && toCancel != null) {
1233 // Fast path: we are adding work to an existing job, and the JobInfo is not
1234 // changing. We can just directly enqueue this work in to the job.
1235 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -08001236
Dianne Hackborn342e6032017-04-13 18:04:31 -07001237 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -08001238
1239 // If any of work item is enqueued when the source is in the foreground,
1240 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -08001241 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001242
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001243 return JobScheduler.RESULT_SUCCESS;
1244 }
1245 }
1246
1247 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -08001248
1249 // Give exemption if the source is in the foreground just now.
1250 // Note if it's a sync job, this method is called on the handler so it's not exactly
1251 // the state when requestSync() was called, but that should be fine because of the
1252 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -08001253 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001254
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001255 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -08001256 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -08001257 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001258 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1259 Slog.w(TAG, "Too many jobs for uid " + uId);
1260 throw new IllegalStateException("Apps may not schedule more than "
1261 + MAX_JOBS_PER_APP + " distinct jobs");
1262 }
1263 }
1264
Dianne Hackborna47223f2017-03-30 13:49:13 -07001265 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001266 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001267
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001268 if (work != null) {
1269 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001270 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001271 }
Christopher Tate16032042018-10-05 15:53:50 -07001272
1273 if (toCancel != null) {
1274 // Implicitly replaces the existing job record with the new instance
1275 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
1276 } else {
1277 startTrackingJobLocked(jobStatus, null);
1278 }
Tej Singhd5747a62018-01-08 20:57:35 -08001279 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -08001280 uId, null, jobStatus.getBatteryName(),
Tej Singh33a412b2018-03-16 18:43:59 -07001281 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1282 JobProtoEnums.STOP_REASON_CANCELLED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001283
1284 // If the job is immediately ready to run, then we can just immediately
1285 // put it in the pending list and try to schedule it. This is especially
1286 // important for jobs with a 0 deadline constraint, since they will happen a fair
1287 // amount, we want to handle them as quickly as possible, and semantically we want to
1288 // make sure we have started holding the wake lock for the job before returning to
1289 // the caller.
1290 // If the job is not yet ready to run, there is nothing more to do -- we are
1291 // now just waiting for one of its controllers to change state and schedule
1292 // the job appropriately.
1293 if (isReadyToBeExecutedLocked(jobStatus)) {
1294 // This is a new job, we can just immediately put it on the pending
1295 // list and try to run it.
1296 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001297 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001298 maybeRunPendingJobsLocked();
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001299 } else {
1300 evaluateControllerStatesLocked(jobStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001301 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001302 }
Christopher Tate7060b042014-06-09 19:50:00 -07001303 return JobScheduler.RESULT_SUCCESS;
1304 }
1305
1306 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001307 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001308 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1309 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
1310 for (int i = jobs.size() - 1; i >= 0; i--) {
1311 JobStatus job = jobs.get(i);
1312 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -07001313 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001314 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -07001315 }
Christopher Tate7060b042014-06-09 19:50:00 -07001316 }
1317
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001318 public JobInfo getPendingJob(int uid, int jobId) {
1319 synchronized (mLock) {
1320 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1321 for (int i = jobs.size() - 1; i >= 0; i--) {
1322 JobStatus job = jobs.get(i);
1323 if (job.getJobId() == jobId) {
1324 return job.getJob();
1325 }
1326 }
1327 return null;
1328 }
1329 }
1330
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001331 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001332 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001333 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
1334 for (int i=0; i<jobsForUser.size(); i++) {
1335 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001336 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001337 }
Christopher Tate7060b042014-06-09 19:50:00 -07001338 }
1339 }
1340
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001341 private void cancelJobsForNonExistentUsers() {
1342 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
1343 synchronized (mLock) {
1344 mJobs.removeJobsOfNonUsers(umi.getUserIds());
1345 }
1346 }
1347
Makoto Onukie7b96182017-08-30 14:53:16 -07001348 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
1349 if ("android".equals(pkgName)) {
1350 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
1351 return;
1352 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001353 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001354 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1355 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1356 final JobStatus job = jobsForUid.get(i);
1357 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001358 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001359 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001360 }
1361 }
1362 }
1363
Christopher Tate7060b042014-06-09 19:50:00 -07001364 /**
1365 * Entry point from client to cancel all jobs originating from their uid.
1366 * This will remove the job from the master list, and cancel the job if it was staged for
1367 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -07001368 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -08001369 *
Christopher Tate7060b042014-06-09 19:50:00 -07001370 */
Christopher Tate8c67d122017-09-29 16:54:26 -07001371 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -07001372 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001373 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -07001374 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -07001375 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001376
1377 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001378 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001379 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1380 for (int i=0; i<jobsForUid.size(); i++) {
1381 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001382 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -07001383 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001384 }
Christopher Tate7060b042014-06-09 19:50:00 -07001385 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001386 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -07001387 }
1388
1389 /**
1390 * Entry point from client to cancel the job corresponding to the jobId provided.
1391 * This will remove the job from the master list, and cancel the job if it was staged for
1392 * execution or being executed.
1393 * @param uid Uid of the calling client.
1394 * @param jobId Id of the job, provided at schedule-time.
1395 */
Makoto Onukid2bfec62018-01-12 13:58:01 -08001396 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -07001397 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001398 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001399 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001400 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001401 cancelJobImplLocked(toCancel, null,
1402 "cancel() called by app, callingUid=" + callingUid
1403 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001404 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001405 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001406 }
1407 }
1408
Christopher Tate16032042018-10-05 15:53:50 -07001409 /**
1410 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob}
1411 * is null, the cancelled job is removed outright from the system. If
1412 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
1413 * currently scheduled jobs.
1414 */
Dianne Hackborn729a3282017-06-09 16:06:01 -07001415 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001416 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1417 cancelled.unprepareLocked(ActivityManager.getService());
1418 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1419 // Remove from pending queue.
1420 if (mPendingJobs.remove(cancelled)) {
1421 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001422 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001423 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001424 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Christopher Tate16032042018-10-05 15:53:50 -07001425 // If this is a replacement, bring in the new version of the job
1426 if (incomingJob != null) {
1427 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
1428 startTrackingJobLocked(incomingJob, cancelled);
1429 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001430 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001431 }
1432
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001433 void updateUidState(int uid, int procState) {
1434 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001435 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1436 // Only use this if we are exactly the top app. All others can live
1437 // with just the foreground priority. This means that persistent processes
1438 // can never be the top app priority... that is fine.
1439 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001440 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
1441 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001442 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001443 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001444 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001445 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001446 }
1447 }
1448 }
1449
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001450 @Override
1451 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001452 synchronized (mLock) {
Makoto Onuki0f6499c2018-12-14 16:34:59 -08001453 if (DEBUG) {
1454 Slog.d(TAG, "Doze state changed: " + deviceIdle);
1455 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001456 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001457 // When becoming idle, make sure no jobs are actively running,
1458 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001459 for (int i=0; i<mActiveServices.size(); i++) {
1460 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001461 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001462 if (executing != null
1463 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001464 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1465 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001466 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001467 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001468 } else {
1469 // When coming out of idle, allow thing to start back up.
1470 if (mReadyToRock) {
1471 if (mLocalDeviceIdleController != null) {
1472 if (!mReportedActive) {
1473 mReportedActive = true;
1474 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001475 }
1476 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001477 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001478 }
1479 }
1480 }
1481 }
1482
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001483 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001484 // active is true if pending queue contains jobs OR some job is running.
1485 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001486 if (mPendingJobs.size() <= 0) {
1487 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001488 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001489 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001490 if (job != null
1491 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001492 && !job.dozeWhitelisted
1493 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001494 // We will report active if we have a job running and it is not an exception
1495 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001496 active = true;
1497 break;
1498 }
1499 }
1500 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001501
1502 if (mReportedActive != active) {
1503 mReportedActive = active;
1504 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001505 mLocalDeviceIdleController.setJobsActive(active);
1506 }
1507 }
1508 }
1509
Christopher Tated117b292018-01-05 17:32:36 -08001510 void reportAppUsage(String packageName, int userId) {
1511 // This app just transitioned into interactive use or near equivalent, so we should
1512 // take a look at its job state for feedback purposes.
1513 }
1514
Christopher Tate7060b042014-06-09 19:50:00 -07001515 /**
1516 * Initializes the system service.
1517 * <p>
1518 * Subclasses must define a single argument constructor that accepts the context
1519 * and passes it to super.
1520 * </p>
1521 *
1522 * @param context The system server context.
1523 */
1524 public JobSchedulerService(Context context) {
1525 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001526
1527 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001528 mActivityManagerInternal = Preconditions.checkNotNull(
1529 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001530
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001531 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001532 mConstants = new Constants();
1533 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001534 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001535
Makoto Onuki714f97d2018-12-05 11:18:13 -08001536 mConcurrencyManager = new JobConcurrencyManager(this);
1537
Christopher Tatea732f012017-10-26 17:26:53 -07001538 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001539 mStandbyTracker = new StandbyTracker();
1540 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1541 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001542
1543 // The job store needs to call back
1544 publishLocalService(JobSchedulerInternal.class, new LocalService());
1545
1546 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001547 mJobs = JobStore.initAndGet(this);
1548
Christopher Tate7060b042014-06-09 19:50:00 -07001549 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001550 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001551 mControllers.add(new ConnectivityController(this));
1552 mControllers.add(new TimeController(this));
1553 mControllers.add(new IdleController(this));
1554 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001555 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001556 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001557 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001558 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001559 mControllers.add(new ContentObserverController(this));
1560 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001561 mControllers.add(mDeviceIdleJobsController);
Kweku Adams4836f9d2018-11-12 17:04:17 -08001562 mControllers.add(new QuotaController(this));
Christopher Tate616541d2017-07-26 14:27:38 -07001563
1564 // If the job store determined that it can't yet reschedule persisted jobs,
1565 // we need to start watching the clock.
1566 if (!mJobs.jobTimesInflatedValid()) {
1567 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1568 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1569 }
Christopher Tate7060b042014-06-09 19:50:00 -07001570 }
1571
Christopher Tate616541d2017-07-26 14:27:38 -07001572 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1573 @Override
1574 public void onReceive(Context context, Intent intent) {
1575 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1576 // When we reach clock sanity, recalculate the temporal windows
1577 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001578 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001579 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1580
1581 // We've done our job now, so stop watching the time.
1582 context.unregisterReceiver(this);
1583
1584 // And kick off the work to update the affected jobs, using a secondary
1585 // thread instead of chugging away here on the main looper thread.
1586 FgThread.getHandler().post(mJobTimeUpdater);
1587 }
1588 }
1589 }
1590 };
1591
1592 private final Runnable mJobTimeUpdater = () -> {
1593 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1594 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1595 synchronized (mLock) {
1596 // Note: we intentionally both look up the existing affected jobs and replace them
1597 // with recalculated ones inside the same lock lifetime.
1598 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1599
1600 // Now, at each position [i], we have both the existing JobStatus
1601 // and the one that replaces it.
1602 final int N = toAdd.size();
1603 for (int i = 0; i < N; i++) {
1604 final JobStatus oldJob = toRemove.get(i);
1605 final JobStatus newJob = toAdd.get(i);
1606 if (DEBUG) {
1607 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1608 }
1609 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1610 }
1611 }
1612 };
1613
Christopher Tate7060b042014-06-09 19:50:00 -07001614 @Override
1615 public void onStart() {
1616 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1617 }
1618
1619 @Override
1620 public void onBootPhase(int phase) {
1621 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001622 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001623
Makoto Onukie4918212018-02-06 11:30:15 -08001624 mAppStateTracker = Preconditions.checkNotNull(
1625 LocalServices.getService(AppStateTracker.class));
Kweku Adams4836f9d2018-11-12 17:04:17 -08001626 if (mConstants.USE_HEARTBEATS) {
1627 setNextHeartbeatAlarm();
1628 }
Makoto Onuki15407842018-01-19 14:23:11 -08001629
Shreyas Basarge5db09082016-01-07 13:38:29 +00001630 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001631 final IntentFilter filter = new IntentFilter();
1632 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1633 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001634 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1635 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001636 filter.addDataScheme("package");
1637 getContext().registerReceiverAsUser(
1638 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1639 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1640 getContext().registerReceiverAsUser(
1641 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001642 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001643 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001644 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001645 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1646 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001647 } catch (RemoteException e) {
1648 // ignored; both services live in system_server
1649 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001650 // Remove any jobs that are not associated with any of the current users.
1651 cancelJobsForNonExistentUsers();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001652 // Register thermal callback
1653 mThermalService = IThermalService.Stub.asInterface(
1654 ServiceManager.getService(Context.THERMAL_SERVICE));
1655 if (mThermalService != null) {
1656 try {
1657 mThermalService.registerThermalStatusListener(new ThermalStatusListener());
1658 } catch (RemoteException e) {
1659 Slog.e(TAG, "Failed to register thermal callback.", e);
1660 }
1661 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001662 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001663 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001664 // Let's go!
1665 mReadyToRock = true;
1666 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1667 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001668 mLocalDeviceIdleController
1669 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001670 // Create the "runners".
1671 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1672 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001673 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001674 getContext().getMainLooper()));
1675 }
1676 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001677 mJobs.forEachJob((job) -> {
1678 for (int controller = 0; controller < mControllers.size(); controller++) {
1679 final StateController sc = mControllers.get(controller);
1680 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001681 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001682 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001683 // GO GO GO!
1684 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1685 }
Christopher Tate7060b042014-06-09 19:50:00 -07001686 }
1687 }
1688
1689 /**
1690 * Called when we have a job status object that we need to insert in our
1691 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1692 * about.
1693 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001694 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1695 if (!jobStatus.isPreparedLocked()) {
1696 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1697 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001698 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001699 final boolean update = mJobs.add(jobStatus);
1700 if (mReadyToRock) {
1701 for (int i = 0; i < mControllers.size(); i++) {
1702 StateController controller = mControllers.get(i);
1703 if (update) {
1704 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001705 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001706 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001707 }
Christopher Tate7060b042014-06-09 19:50:00 -07001708 }
1709 }
1710
1711 /**
1712 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1713 * object removed.
1714 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001715 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001716 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001717 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001718 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001719
1720 // Remove from store as well as controllers.
1721 final boolean removed = mJobs.remove(jobStatus, writeBack);
1722 if (removed && mReadyToRock) {
1723 for (int i=0; i<mControllers.size(); i++) {
1724 StateController controller = mControllers.get(i);
1725 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001726 }
1727 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001728 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001729 }
1730
Dianne Hackborn729a3282017-06-09 16:06:01 -07001731 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001732 for (int i=0; i<mActiveServices.size(); i++) {
1733 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001734 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001735 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001736 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001737 return true;
1738 }
1739 }
1740 return false;
1741 }
1742
1743 /**
1744 * @param job JobStatus we are querying against.
1745 * @return Whether or not the job represented by the status object is currently being run or
1746 * is pending.
1747 */
1748 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001749 for (int i=0; i<mActiveServices.size(); i++) {
1750 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001751 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001752 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1753 return true;
1754 }
1755 }
1756 return false;
1757 }
1758
Dianne Hackborn807de782016-04-07 17:54:41 -07001759 void noteJobsPending(List<JobStatus> jobs) {
1760 for (int i = jobs.size() - 1; i >= 0; i--) {
1761 JobStatus job = jobs.get(i);
1762 mJobPackageTracker.notePending(job);
1763 }
1764 }
1765
1766 void noteJobsNonpending(List<JobStatus> jobs) {
1767 for (int i = jobs.size() - 1; i >= 0; i--) {
1768 JobStatus job = jobs.get(i);
1769 mJobPackageTracker.noteNonpending(job);
1770 }
1771 }
1772
Christopher Tate7060b042014-06-09 19:50:00 -07001773 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001774 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1775 * specify an override deadline on a failed job (the failed job will run even though it's not
1776 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1777 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1778 *
Christopher Tate7060b042014-06-09 19:50:00 -07001779 * @param failureToReschedule Provided job status that we will reschedule.
1780 * @return A newly instantiated JobStatus with the same constraints as the last job except
1781 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001782 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001783 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001784 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001785 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001786 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001787 final JobInfo job = failureToReschedule.getJob();
1788
1789 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001790 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1791 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001792
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001793 if (failureToReschedule.hasWorkLocked()) {
1794 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1795 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1796 + backoffAttempts + " > work limit "
1797 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1798 return null;
1799 }
1800 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1801 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1802 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1803 return null;
1804 }
1805
Christopher Tate7060b042014-06-09 19:50:00 -07001806 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001807 case JobInfo.BACKOFF_POLICY_LINEAR: {
1808 long backoff = initialBackoffMillis;
1809 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1810 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1811 }
1812 delayMillis = backoff * backoffAttempts;
1813 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001814 default:
1815 if (DEBUG) {
1816 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1817 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001818 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1819 long backoff = initialBackoffMillis;
1820 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1821 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1822 }
1823 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1824 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001825 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001826 delayMillis =
1827 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001828 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1829 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001830 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001831 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001832 for (int ic=0; ic<mControllers.size(); ic++) {
1833 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001834 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001835 }
1836 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001837 }
1838
1839 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001840 * Called after a periodic has executed so we can reschedule it. We take the last execution
1841 * time of the job to be the time of completion (i.e. the time at which this function is
1842 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001843 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001844 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1845 * to underscheduling at least, rather than if we had taken the last execution time to be the
1846 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001847 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1848 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001849 * @return A new job representing the execution criteria for this instantiation of the
1850 * recurring job.
1851 */
1852 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001853 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001854 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001855 long runEarly = 0L;
1856
1857 // If this periodic was rescheduled it won't have a deadline.
1858 if (periodicToReschedule.hasDeadlineConstraint()) {
1859 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1860 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001861 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001862 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001863 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1864 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001865
1866 if (DEBUG) {
1867 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1868 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1869 }
Christopher Tatea732f012017-10-26 17:26:53 -07001870 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1871 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1872 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001873 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001874 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001875 }
1876
Christopher Tate325768c2018-03-07 16:07:56 -08001877 /*
1878 * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1879 * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1880 * reboot behavior.
1881 */
Christopher Tated1aebb32018-01-31 13:24:14 -08001882 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
Christopher Tate325768c2018-03-07 16:07:56 -08001883 // The furthest back in pre-boot time that we need to bother with
1884 long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1885 boolean cacheHit = false;
Christopher Tated1aebb32018-01-31 13:24:14 -08001886 synchronized (mLock) {
Christopher Tate325768c2018-03-07 16:07:56 -08001887 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1888 if (jobPackages != null) {
1889 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1890 if (cachedValue < Long.MAX_VALUE) {
1891 cacheHit = true;
1892 heartbeat = cachedValue;
1893 }
1894 }
1895 if (!cacheHit) {
1896 // We haven't seen it yet; ask usage stats about it
1897 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1898 if (timeSinceJob < Long.MAX_VALUE) {
1899 // Usage stats knows about it from before, so calculate back from that
1900 // and go from there.
1901 heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1902 }
1903 // If usage stats returned its "not found" MAX_VALUE, we still have the
1904 // negative default 'heartbeat' value we established above
1905 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1906 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001907 }
1908 if (DEBUG_STANDBY) {
Christopher Tate325768c2018-03-07 16:07:56 -08001909 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1910 + packageName + "/" + userId);
Christopher Tated1aebb32018-01-31 13:24:14 -08001911 }
1912 return heartbeat;
1913 }
1914
1915 long heartbeatWhenJobsLastRun(JobStatus job) {
1916 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1917 }
1918
Christopher Tate325768c2018-03-07 16:07:56 -08001919 void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1920 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1921 if (jobPackages == null) {
1922 jobPackages = new HashMap<>();
1923 mLastJobHeartbeats.put(userId, jobPackages);
1924 }
1925 jobPackages.put(packageName, heartbeat);
1926 }
1927
Christopher Tate7060b042014-06-09 19:50:00 -07001928 // JobCompletedListener implementations.
1929
1930 /**
1931 * A job just finished executing. We fetch the
1932 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001933 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001934 * @param jobStatus Completed job.
1935 * @param needsReschedule Whether the implementing class should reschedule this job.
1936 */
1937 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001938 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001939 if (DEBUG) {
1940 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1941 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001942
1943 // If the job wants to be rescheduled, we first need to make the next upcoming
1944 // job so we can transfer any appropriate state over from the previous job when
1945 // we stop it.
1946 final JobStatus rescheduledJob = needsReschedule
1947 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1948
Shreyas Basarge73f10252016-02-11 17:06:13 +00001949 // Do not write back immediately if this is a periodic job. The job may get lost if system
1950 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001951 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001952 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001953 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001954 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001955 // We still want to check for jobs to execute, because this job may have
1956 // scheduled a new job under the same job id, and now we can run it.
1957 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001958 return;
1959 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001960
1961 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001962 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001963 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001964 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001965 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001966 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001967 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001968 } else if (jobStatus.getJob().isPeriodic()) {
1969 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001970 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001971 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001972 } catch (SecurityException e) {
1973 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1974 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001975 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001976 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001977 jobStatus.unprepareLocked(ActivityManager.getService());
1978 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001979 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001980 }
1981
1982 // StateChangedListener implementations.
1983
1984 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001985 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1986 * some controller's state has changed, so as to run through the list of jobs and start/stop
1987 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001988 */
1989 @Override
1990 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001991 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001992 }
1993
1994 @Override
1995 public void onRunJobNow(JobStatus jobStatus) {
1996 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1997 }
1998
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001999 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07002000
2001 public JobHandler(Looper looper) {
2002 super(looper);
2003 }
2004
2005 @Override
2006 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002007 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07002008 if (!mReadyToRock) {
2009 return;
2010 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002011 switch (message.what) {
2012 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07002013 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07002014 // runNow can be null, which is a controller's way of indicating that its
2015 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07002016 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002017 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002018 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002019 } else {
2020 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002021 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002022 } break;
2023 case MSG_CHECK_JOB:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08002024 if (DEBUG) {
2025 Slog.d(TAG, "MSG_CHECK_JOB");
2026 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002027 if (mReportedActive) {
2028 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002029 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002030 } else {
2031 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002032 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00002033 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002034 break;
2035 case MSG_CHECK_JOB_GREEDY:
Makoto Onuki0f6499c2018-12-14 16:34:59 -08002036 if (DEBUG) {
2037 Slog.d(TAG, "MSG_CHECK_JOB_GREEDY");
2038 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002039 queueReadyJobsForExecutionLocked();
2040 break;
2041 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07002042 cancelJobImplLocked((JobStatus) message.obj, null,
2043 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002044 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08002045
2046 case MSG_UID_STATE_CHANGED: {
2047 final int uid = message.arg1;
2048 final int procState = message.arg2;
2049 updateUidState(uid, procState);
2050 break;
2051 }
2052 case MSG_UID_GONE: {
2053 final int uid = message.arg1;
2054 final boolean disabled = message.arg2 != 0;
2055 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
2056 if (disabled) {
2057 cancelJobsForUid(uid, "uid gone");
2058 }
2059 synchronized (mLock) {
2060 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2061 }
2062 break;
2063 }
2064 case MSG_UID_ACTIVE: {
2065 final int uid = message.arg1;
2066 synchronized (mLock) {
2067 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
2068 }
2069 break;
2070 }
2071 case MSG_UID_IDLE: {
2072 final int uid = message.arg1;
2073 final boolean disabled = message.arg2 != 0;
2074 if (disabled) {
2075 cancelJobsForUid(uid, "app uid idle");
2076 }
2077 synchronized (mLock) {
2078 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
2079 }
2080 break;
2081 }
2082
Matthew Williams75fc5252014-09-02 16:17:53 -07002083 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002084 maybeRunPendingJobsLocked();
2085 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
2086 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07002087 }
2088 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002089 }
Christopher Tate7060b042014-06-09 19:50:00 -07002090
Wei Wang8c0c3c12018-11-14 14:56:52 -08002091 private boolean isJobThermalConstrainedLocked(JobStatus job) {
2092 return mThermalConstraint && job.hasConnectivityConstraint()
2093 && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
2094 }
2095
Dianne Hackborn6d068262017-05-16 13:14:37 -07002096 private void stopNonReadyActiveJobsLocked() {
2097 for (int i=0; i<mActiveServices.size(); i++) {
2098 JobServiceContext serviceContext = mActiveServices.get(i);
2099 final JobStatus running = serviceContext.getRunningJobLocked();
Wei Wang8c0c3c12018-11-14 14:56:52 -08002100 if (running == null) {
2101 continue;
2102 }
2103 if (!running.isReady()) {
Dianne Hackborn6d068262017-05-16 13:14:37 -07002104 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07002105 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
2106 "cancelled due to unsatisfied constraints");
Wei Wang8c0c3c12018-11-14 14:56:52 -08002107 } else if (isJobThermalConstrainedLocked(running)) {
2108 serviceContext.cancelExecutingJobLocked(
2109 JobParameters.REASON_DEVICE_THERMAL,
2110 "cancelled due to thermal condition");
Dianne Hackborn6d068262017-05-16 13:14:37 -07002111 }
2112 }
2113 }
2114
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002115 /**
2116 * Run through list of jobs and execute all possible - at least one is expired so we do
2117 * as many as we can.
2118 */
2119 private void queueReadyJobsForExecutionLocked() {
2120 if (DEBUG) {
2121 Slog.d(TAG, "queuing all ready jobs for execution:");
2122 }
2123 noteJobsNonpending(mPendingJobs);
2124 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002125 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002126 mJobs.forEachJob(mReadyQueueFunctor);
2127 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08002128
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002129 if (DEBUG) {
2130 final int queuedJobs = mPendingJobs.size();
2131 if (queuedJobs == 0) {
2132 Slog.d(TAG, "No jobs pending.");
2133 } else {
2134 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08002135 }
2136 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002137 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002138
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002139 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002140 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08002141
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002142 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002143 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002144 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07002145 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002146 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07002147 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002148 if (newReadyJobs == null) {
2149 newReadyJobs = new ArrayList<JobStatus>();
2150 }
2151 newReadyJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002152 } else {
2153 evaluateControllerStatesLocked(job);
Christopher Tate7060b042014-06-09 19:50:00 -07002154 }
2155 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002156
2157 public void postProcess() {
2158 if (newReadyJobs != null) {
2159 noteJobsPending(newReadyJobs);
2160 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002161 if (mPendingJobs.size() > 1) {
2162 mPendingJobs.sort(mEnqueueTimeComparator);
2163 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002164 }
2165 newReadyJobs = null;
2166 }
2167 }
2168 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
2169
2170 /**
2171 * The state of at least one job has changed. Here is where we could enforce various
2172 * policies on when we want to execute jobs.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002173 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002174 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002175 int chargingCount;
2176 int batteryNotLowCount;
2177 int storageNotLowCount;
2178 int idleCount;
2179 int backoffCount;
2180 int connectivityCount;
2181 int contentCount;
2182 List<JobStatus> runnableJobs;
2183
2184 public MaybeReadyJobQueueFunctor() {
2185 reset();
2186 }
2187
2188 // Functor method invoked for each job via JobStore.forEachJob()
2189 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002190 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002191 if (isReadyToBeExecutedLocked(job)) {
2192 try {
2193 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
2194 job.getJob().getService().getPackageName())) {
2195 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
2196 + job.getJob().toString() + " -- package not allowed to start");
2197 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
2198 return;
2199 }
2200 } catch (RemoteException e) {
2201 }
2202 if (job.getNumFailures() > 0) {
2203 backoffCount++;
2204 }
2205 if (job.hasIdleConstraint()) {
2206 idleCount++;
2207 }
2208 if (job.hasConnectivityConstraint()) {
2209 connectivityCount++;
2210 }
2211 if (job.hasChargingConstraint()) {
2212 chargingCount++;
2213 }
2214 if (job.hasBatteryNotLowConstraint()) {
2215 batteryNotLowCount++;
2216 }
2217 if (job.hasStorageNotLowConstraint()) {
2218 storageNotLowCount++;
2219 }
2220 if (job.hasContentTriggerConstraint()) {
2221 contentCount++;
2222 }
2223 if (runnableJobs == null) {
2224 runnableJobs = new ArrayList<>();
2225 }
2226 runnableJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002227 } else {
2228 evaluateControllerStatesLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002229 }
2230 }
2231
2232 public void postProcess() {
2233 if (backoffCount > 0 ||
2234 idleCount >= mConstants.MIN_IDLE_COUNT ||
2235 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
2236 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
2237 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
2238 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
2239 contentCount >= mConstants.MIN_CONTENT_COUNT ||
2240 (runnableJobs != null
2241 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
2242 if (DEBUG) {
2243 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
2244 }
2245 noteJobsPending(runnableJobs);
2246 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002247 if (mPendingJobs.size() > 1) {
2248 mPendingJobs.sort(mEnqueueTimeComparator);
2249 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002250 } else {
2251 if (DEBUG) {
2252 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
2253 }
2254 }
2255
2256 // Be ready for next time
2257 reset();
2258 }
2259
2260 private void reset() {
2261 chargingCount = 0;
2262 idleCount = 0;
2263 backoffCount = 0;
2264 connectivityCount = 0;
2265 batteryNotLowCount = 0;
2266 storageNotLowCount = 0;
2267 contentCount = 0;
2268 runnableJobs = null;
2269 }
2270 }
2271 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
2272
2273 private void maybeQueueReadyJobsForExecutionLocked() {
2274 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
2275
2276 noteJobsNonpending(mPendingJobs);
2277 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002278 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002279 mJobs.forEachJob(mMaybeQueueFunctor);
2280 mMaybeQueueFunctor.postProcess();
2281 }
2282
Christopher Tated1aebb32018-01-31 13:24:14 -08002283 /**
2284 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
2285 */
2286 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
2287
2288 @Override
2289 public void onAlarm() {
2290 synchronized (mLock) {
2291 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
2292 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
2293 if (beatsElapsed > 0) {
2294 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
2295 advanceHeartbeatLocked(beatsElapsed);
2296 }
Christopher Tatea732f012017-10-26 17:26:53 -07002297 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002298 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07002299 }
Christopher Tatea732f012017-10-26 17:26:53 -07002300 }
2301
Christopher Tated1aebb32018-01-31 13:24:14 -08002302 // Intentionally does not touch the alarm timing
2303 void advanceHeartbeatLocked(long beatsElapsed) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002304 if (!mConstants.USE_HEARTBEATS) {
2305 return;
2306 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002307 mHeartbeat += beatsElapsed;
2308 if (DEBUG_STANDBY) {
2309 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
2310 + " to " + mHeartbeat);
2311 }
2312 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
2313 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
2314 // new jobs scheduled by apps in that bucket will be permitted to run
2315 // immediately.
2316 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07002317 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002318 // Did we reach or cross a bucket boundary?
2319 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
2320 didAdvanceBucket = true;
2321 }
2322 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07002323 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
2324 }
2325 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002326 Slog.v(TAG, " Bucket " + i + " next heartbeat "
2327 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07002328 }
2329 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002330
2331 if (didAdvanceBucket) {
2332 if (DEBUG_STANDBY) {
2333 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
2334 }
2335 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2336 }
2337 }
2338
2339 void setNextHeartbeatAlarm() {
2340 final long heartbeatLength;
2341 synchronized (mLock) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002342 if (!mConstants.USE_HEARTBEATS) {
2343 return;
2344 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002345 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
2346 }
2347 final long now = sElapsedRealtimeClock.millis();
2348 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
2349 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
2350 if (DEBUG_STANDBY) {
2351 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
2352 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
2353 }
2354 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
2355 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
2356 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07002357 }
2358
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002359 /** Returns true if both the calling and source users for the job are started. */
2360 private boolean areUsersStartedLocked(final JobStatus job) {
2361 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
2362 if (job.getUserId() == job.getSourceUserId()) {
2363 return sourceStarted;
2364 }
2365 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
2366 }
2367
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002368 /**
2369 * Criteria for moving a job into the pending queue:
2370 * - It's ready.
2371 * - It's not pending.
2372 * - It's not already running on a JSC.
2373 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07002374 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002375 * - The component is enabled and runnable.
2376 */
2377 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002378 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002379
2380 if (DEBUG) {
2381 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2382 + " ready=" + jobReady);
2383 }
2384
2385 // This is a condition that is very likely to be false (most jobs that are
2386 // scheduled are sitting there, not ready yet) and very cheap to check (just
2387 // a few conditions on data in JobStatus).
2388 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08002389 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
2390 Slog.v(TAG, " NOT READY: " + job);
2391 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002392 return false;
2393 }
2394
2395 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002396
Kweku Adams8bd5edc2018-12-07 18:33:39 -08002397 final boolean userStarted = areUsersStartedLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002398
2399 if (DEBUG) {
2400 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002401 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002402 }
2403
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002404 // These are also fairly cheap to check, though they typically will not
2405 // be conditions we fail.
2406 if (!jobExists || !userStarted) {
2407 return false;
2408 }
2409
Wei Wang8c0c3c12018-11-14 14:56:52 -08002410 if (isJobThermalConstrainedLocked(job)) {
2411 return false;
2412 }
2413
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002414 final boolean jobPending = mPendingJobs.contains(job);
2415 final boolean jobActive = isCurrentlyActiveLocked(job);
2416
2417 if (DEBUG) {
2418 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2419 + " pending=" + jobPending + " active=" + jobActive);
2420 }
2421
2422 // These can be a little more expensive (especially jobActive, since we need to
2423 // go through the array of all potentially active jobs), so we are doing them
2424 // later... but still before checking with the package manager!
2425 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002426 return false;
2427 }
2428
Kweku Adams4836f9d2018-11-12 17:04:17 -08002429 if (mConstants.USE_HEARTBEATS) {
2430 // If the app is in a non-active standby bucket, make sure we've waited
2431 // an appropriate amount of time since the last invocation. During device-
2432 // wide parole, standby bucketing is ignored.
2433 //
2434 // Jobs in 'active' apps are not subject to standby, nor are jobs that are
2435 // specifically marked as exempt.
Christopher Tate20afddd2018-02-28 15:19:19 -08002436 if (DEBUG_STANDBY) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002437 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2438 + " parole=" + mInParole + " active=" + job.uidActive
2439 + " exempt=" + job.getJob().isExemptedFromAppStandby());
Christopher Tate20afddd2018-02-28 15:19:19 -08002440 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002441 if (!mInParole
2442 && !job.uidActive
2443 && !job.getJob().isExemptedFromAppStandby()) {
2444 final int bucket = job.getStandbyBucket();
2445 if (DEBUG_STANDBY) {
2446 Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
2447 + " next=" + mNextBucketHeartbeat[bucket]);
2448 }
2449 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
2450 // Only skip this job if the app is still waiting for the end of its nominal
2451 // bucket interval. Once it's waited that long, we let it go ahead and clear.
2452 // The final (NEVER) bucket is special; we never age those apps' jobs into
2453 // runnability.
2454 final long appLastRan = heartbeatWhenJobsLastRun(job);
2455 if (bucket >= mConstants.STANDBY_BEATS.length
2456 || (mHeartbeat > appLastRan
2457 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
2458 // TODO: log/trace that we're deferring the job due to bucketing if we
2459 // hit this
2460 if (job.getWhenStandbyDeferred() == 0) {
2461 if (DEBUG_STANDBY) {
2462 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
2463 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2464 + " for " + job);
2465 }
2466 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002467 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002468 return false;
2469 } else {
2470 if (DEBUG_STANDBY) {
2471 Slog.v(TAG, "Bucket deferred job aged into runnability at "
2472 + mHeartbeat + " : " + job);
2473 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002474 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08002475 }
Christopher Tatea732f012017-10-26 17:26:53 -07002476 }
Christopher Tatea732f012017-10-26 17:26:53 -07002477 }
2478
2479 // The expensive check last: validate that the defined package+service is
2480 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002481 final boolean componentPresent;
2482 try {
2483 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2484 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
Kweku Adams8bd5edc2018-12-07 18:33:39 -08002485 job.getUserId()) != null);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002486 } catch (RemoteException e) {
2487 throw e.rethrowAsRuntimeException();
2488 }
2489
2490 if (DEBUG) {
2491 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2492 + " componentPresent=" + componentPresent);
2493 }
2494
2495 // Everything else checked out so far, so this is the final yes/no check
2496 return componentPresent;
2497 }
2498
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002499 private void evaluateControllerStatesLocked(final JobStatus job) {
2500 for (int c = mControllers.size() - 1; c >= 0; --c) {
2501 final StateController sc = mControllers.get(c);
2502 sc.evaluateStateLocked(job);
2503 }
2504 }
2505
2506 /**
2507 * Returns true if non-job constraint components are in place -- if job.isReady() returns true
2508 * and this method returns true, then the job is ready to be executed.
2509 */
2510 public boolean areComponentsInPlaceLocked(JobStatus job) {
2511 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
2512 // conditions.
2513
2514 final boolean jobExists = mJobs.containsJob(job);
2515 final boolean userStarted = areUsersStartedLocked(job);
2516
2517 if (DEBUG) {
2518 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2519 + " exists=" + jobExists + " userStarted=" + userStarted);
2520 }
2521
2522 // These are also fairly cheap to check, though they typically will not
2523 // be conditions we fail.
2524 if (!jobExists || !userStarted) {
2525 return false;
2526 }
2527
2528 // Job pending/active doesn't affect the readiness of a job.
2529
2530 // Skipping the hearbeat check as this will only come into play when using the rolling
2531 // window quota management system.
2532
2533 // The expensive check last: validate that the defined package+service is
2534 // still present & viable.
2535 final boolean componentPresent;
2536 try {
2537 // TODO: cache result until we're notified that something in the package changed.
2538 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2539 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2540 job.getUserId()) != null);
2541 } catch (RemoteException e) {
2542 throw e.rethrowAsRuntimeException();
2543 }
2544
2545 if (DEBUG) {
2546 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2547 + " componentPresent=" + componentPresent);
2548 }
2549
2550 // Everything else checked out so far, so this is the final yes/no check
2551 return componentPresent;
2552 }
2553
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002554 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002555 * Reconcile jobs in the pending queue against available execution contexts.
2556 * A controller can force a job into the pending queue even if it's already running, but
2557 * here is where we decide whether to actually execute it.
2558 */
2559 private void maybeRunPendingJobsLocked() {
2560 if (DEBUG) {
2561 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2562 }
Makoto Onuki714f97d2018-12-05 11:18:13 -08002563 mConcurrencyManager.assignJobsToContextsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002564 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002565 }
2566
Dianne Hackborn807de782016-04-07 17:54:41 -07002567 private int adjustJobPriority(int curPriority, JobStatus job) {
2568 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2569 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002570 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002571 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002572 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002573 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2574 }
2575 }
2576 return curPriority;
2577 }
2578
Makoto Onuki714f97d2018-12-05 11:18:13 -08002579 int evaluateJobPriorityLocked(JobStatus job) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002580 int priority = job.getPriority();
Makoto Onukiec8b14d2018-12-05 13:22:24 -08002581 if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002582 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002583 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002584 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2585 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002586 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002587 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002588 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002589 }
2590
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002591 final class LocalService implements JobSchedulerInternal {
2592
2593 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002594 * The current bucket heartbeat ordinal
2595 */
2596 public long currentHeartbeat() {
2597 return getCurrentHeartbeat();
2598 }
2599
2600 /**
2601 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2602 */
2603 public long nextHeartbeatForBucket(int bucket) {
2604 synchronized (mLock) {
2605 return mNextBucketHeartbeat[bucket];
2606 }
2607 }
2608
2609 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002610 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2611 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2612 * jobs in a long time is immediately runnable even if the app is bucketed into
2613 * an infrequent time allocation.
2614 */
2615 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2616 final int appStandbyBucket) {
2617 if (appStandbyBucket == 0 ||
2618 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2619 // ACTIVE => everything can be run right away
2620 // NEVER => we won't run them anyway, so let them go in the future
2621 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002622 if (DEBUG_STANDBY) {
2623 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2624 + packageName + "/" + userId);
2625 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002626 return 0;
2627 }
2628
Christopher Tated1aebb32018-01-31 13:24:14 -08002629 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2630 if (DEBUG_STANDBY) {
2631 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2632 + packageName + "/" + userId);
2633 }
2634 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002635 }
2636
Christopher Tate325768c2018-03-07 16:07:56 -08002637 public void noteJobStart(String packageName, int userId) {
2638 synchronized (mLock) {
2639 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2640 }
2641 }
2642
Christopher Tate435c2f42018-01-18 12:59:15 -08002643 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002644 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2645 * jobs are always considered pending.
2646 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002647 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002648 public List<JobInfo> getSystemScheduledPendingJobs() {
2649 synchronized (mLock) {
2650 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002651 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2652 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2653 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002654 }
2655 });
2656 return pendingJobs;
2657 }
2658 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002659
2660 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002661 public void cancelJobsForUid(int uid, String reason) {
2662 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2663 }
2664
2665 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002666 public void addBackingUpUid(int uid) {
2667 synchronized (mLock) {
2668 // No need to actually do anything here, since for a full backup the
2669 // activity manager will kill the process which will kill the job (and
2670 // cause it to restart, but now it can't run).
2671 mBackingUpUids.put(uid, uid);
2672 }
2673 }
2674
2675 @Override
2676 public void removeBackingUpUid(int uid) {
2677 synchronized (mLock) {
2678 mBackingUpUids.delete(uid);
2679 // If there are any jobs for this uid, we need to rebuild the pending list
2680 // in case they are now ready to run.
2681 if (mJobs.countJobsForUid(uid) > 0) {
2682 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2683 }
2684 }
2685 }
2686
2687 @Override
2688 public void clearAllBackingUpUids() {
2689 synchronized (mLock) {
2690 if (mBackingUpUids.size() > 0) {
2691 mBackingUpUids.clear();
2692 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2693 }
2694 }
2695 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002696
2697 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002698 public void reportAppUsage(String packageName, int userId) {
2699 JobSchedulerService.this.reportAppUsage(packageName, userId);
2700 }
2701
2702 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002703 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002704 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002705 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002706 }
2707 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002708 }
2709
Shreyas Basarge5db09082016-01-07 13:38:29 +00002710 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002711 * Tracking of app assignments to standby buckets
2712 */
2713 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002714
Christopher Tatea732f012017-10-26 17:26:53 -07002715 // AppIdleStateChangeListener interface for live updates
2716
2717 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002718 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002719 boolean idle, int bucket, int reason) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002720 // QuotaController handles this now.
Christopher Tatea732f012017-10-26 17:26:53 -07002721 }
2722
2723 @Override
2724 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002725 if (DEBUG_STANDBY) {
2726 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2727 }
2728 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002729 }
Christopher Tated117b292018-01-05 17:32:36 -08002730
2731 @Override
2732 public void onUserInteractionStarted(String packageName, int userId) {
2733 final int uid = mLocalPM.getPackageUid(packageName,
2734 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2735 if (uid < 0) {
2736 // Quietly ignore; the case is already logged elsewhere
2737 return;
2738 }
2739
Amith Yamasani977e11f2018-02-16 11:29:54 -08002740 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2741 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2742 // Too long ago, not worth logging
2743 sinceLast = 0L;
2744 }
Christopher Tated117b292018-01-05 17:32:36 -08002745 final DeferredJobCounter counter = new DeferredJobCounter();
2746 synchronized (mLock) {
2747 mJobs.forEachJobForSourceUid(uid, counter);
2748 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002749 if (counter.numDeferred() > 0 || sinceLast > 0) {
2750 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2751 (BatteryStatsInternal.class);
2752 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
Yangster-mac96353002018-09-05 11:18:55 -07002753 StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
2754 counter.numDeferred(), sinceLast);
Amith Yamasani977e11f2018-02-16 11:29:54 -08002755 }
Christopher Tated117b292018-01-05 17:32:36 -08002756 }
2757 }
2758
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002759 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002760 private int mDeferred = 0;
2761
2762 public int numDeferred() {
2763 return mDeferred;
2764 }
2765
2766 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002767 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002768 if (job.getWhenStandbyDeferred() > 0) {
2769 mDeferred++;
2770 }
2771 }
Christopher Tatea732f012017-10-26 17:26:53 -07002772 }
2773
2774 public static int standbyBucketToBucketIndex(int bucket) {
2775 // Normalize AppStandby constants to indices into our bookkeeping
Christopher Tatef2159712018-03-27 16:04:14 -07002776 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2777 else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2778 else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2779 else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2780 else return ACTIVE_INDEX;
Christopher Tatea732f012017-10-26 17:26:53 -07002781 }
2782
Christopher Tated1aebb32018-01-31 13:24:14 -08002783 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002784 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2785 UsageStatsManagerInternal usageStats = LocalServices.getService(
2786 UsageStatsManagerInternal.class);
2787 int bucket = usageStats != null
2788 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2789 : 0;
2790
2791 bucket = standbyBucketToBucketIndex(bucket);
2792
2793 if (DEBUG_STANDBY) {
2794 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2795 }
2796 return bucket;
2797 }
2798
2799 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002800 * Binder stub trampoline implementation
2801 */
2802 final class JobSchedulerStub extends IJobScheduler.Stub {
2803 /** Cache determination of whether a given app can persist jobs
2804 * key is uid of the calling app; value is undetermined/true/false
2805 */
2806 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2807
2808 // Enforce that only the app itself (or shared uid participant) can schedule a
2809 // job that runs one of the app's services, as well as verifying that the
2810 // named service properly requires the BIND_JOB_SERVICE permission
2811 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002812 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002813 final ComponentName service = job.getService();
2814 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002815 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002816 PackageManager.MATCH_DIRECT_BOOT_AWARE
2817 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002818 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002819 if (si == null) {
2820 throw new IllegalArgumentException("No such service " + service);
2821 }
Christopher Tate7060b042014-06-09 19:50:00 -07002822 if (si.applicationInfo.uid != uid) {
2823 throw new IllegalArgumentException("uid " + uid +
2824 " cannot schedule job in " + service.getPackageName());
2825 }
2826 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2827 throw new IllegalArgumentException("Scheduled service " + service
2828 + " does not require android.permission.BIND_JOB_SERVICE permission");
2829 }
Christopher Tate5568f542014-06-18 13:53:31 -07002830 } catch (RemoteException e) {
2831 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002832 }
2833 }
2834
2835 private boolean canPersistJobs(int pid, int uid) {
2836 // If we get this far we're good to go; all we need to do now is check
2837 // whether the app is allowed to persist its scheduled work.
2838 final boolean canPersist;
2839 synchronized (mPersistCache) {
2840 Boolean cached = mPersistCache.get(uid);
2841 if (cached != null) {
2842 canPersist = cached.booleanValue();
2843 } else {
2844 // Persisting jobs is tantamount to running at boot, so we permit
2845 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2846 // permission
2847 int result = getContext().checkPermission(
2848 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2849 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2850 mPersistCache.put(uid, canPersist);
2851 }
2852 }
2853 return canPersist;
2854 }
2855
Makoto Onuki959acb52018-01-26 14:10:03 -08002856 private void validateJobFlags(JobInfo job, int callingUid) {
2857 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2858 getContext().enforceCallingOrSelfPermission(
2859 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2860 }
2861 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2862 if (callingUid != Process.SYSTEM_UID) {
2863 throw new SecurityException("Job has invalid flags");
2864 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002865 if (job.isPeriodic()) {
2866 Slog.wtf(TAG, "Periodic jobs mustn't have"
2867 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002868 }
2869 }
2870 }
2871
Christopher Tate7060b042014-06-09 19:50:00 -07002872 // IJobScheduler implementation
2873 @Override
2874 public int schedule(JobInfo job) throws RemoteException {
2875 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002876 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002877 }
2878 final int pid = Binder.getCallingPid();
2879 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002880 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002881
2882 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002883 if (job.isPersisted()) {
2884 if (!canPersistJobs(pid, uid)) {
2885 throw new IllegalArgumentException("Error: requested job be persisted without"
2886 + " holding RECEIVE_BOOT_COMPLETED permission.");
2887 }
2888 }
Christopher Tate7060b042014-06-09 19:50:00 -07002889
Makoto Onuki959acb52018-01-26 14:10:03 -08002890 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002891
Christopher Tate7060b042014-06-09 19:50:00 -07002892 long ident = Binder.clearCallingIdentity();
2893 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002894 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2895 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002896 } finally {
2897 Binder.restoreCallingIdentity(ident);
2898 }
2899 }
2900
2901 // IJobScheduler implementation
2902 @Override
2903 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2904 if (DEBUG) {
2905 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2906 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002907 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002908 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002909
2910 enforceValidJobRequest(uid, job);
2911 if (job.isPersisted()) {
2912 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2913 }
2914 if (work == null) {
2915 throw new NullPointerException("work is null");
2916 }
2917
Makoto Onuki959acb52018-01-26 14:10:03 -08002918 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002919
2920 long ident = Binder.clearCallingIdentity();
2921 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002922 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2923 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002924 } finally {
2925 Binder.restoreCallingIdentity(ident);
2926 }
2927 }
2928
2929 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002930 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002931 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002932 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002933 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002934 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002935 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002936 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002937
2938 if (packageName == null) {
2939 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002940 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002941
2942 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2943 android.Manifest.permission.UPDATE_DEVICE_STATS);
2944 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2945 throw new SecurityException("Caller uid " + callerUid
2946 + " not permitted to schedule jobs for other apps");
2947 }
2948
Makoto Onuki959acb52018-01-26 14:10:03 -08002949 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002950
Shreyas Basarge968ac752016-01-11 23:09:26 +00002951 long ident = Binder.clearCallingIdentity();
2952 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002953 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002954 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002955 } finally {
2956 Binder.restoreCallingIdentity(ident);
2957 }
2958 }
2959
2960 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002961 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2962 final int uid = Binder.getCallingUid();
2963
2964 long ident = Binder.clearCallingIdentity();
2965 try {
2966 return JobSchedulerService.this.getPendingJobs(uid);
2967 } finally {
2968 Binder.restoreCallingIdentity(ident);
2969 }
2970 }
2971
2972 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002973 public JobInfo getPendingJob(int jobId) throws RemoteException {
2974 final int uid = Binder.getCallingUid();
2975
2976 long ident = Binder.clearCallingIdentity();
2977 try {
2978 return JobSchedulerService.this.getPendingJob(uid, jobId);
2979 } finally {
2980 Binder.restoreCallingIdentity(ident);
2981 }
2982 }
2983
2984 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002985 public void cancelAll() throws RemoteException {
2986 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002987 long ident = Binder.clearCallingIdentity();
2988 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002989 JobSchedulerService.this.cancelJobsForUid(uid,
2990 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002991 } finally {
2992 Binder.restoreCallingIdentity(ident);
2993 }
2994 }
2995
2996 @Override
2997 public void cancel(int jobId) throws RemoteException {
2998 final int uid = Binder.getCallingUid();
2999
3000 long ident = Binder.clearCallingIdentity();
3001 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08003002 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07003003 } finally {
3004 Binder.restoreCallingIdentity(ident);
3005 }
3006 }
3007
3008 /**
3009 * "dumpsys" infrastructure
3010 */
3011 @Override
3012 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06003013 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07003014
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003015 int filterUid = -1;
3016 boolean proto = false;
3017 if (!ArrayUtils.isEmpty(args)) {
3018 int opti = 0;
3019 while (opti < args.length) {
3020 String arg = args[opti];
3021 if ("-h".equals(arg)) {
3022 dumpHelp(pw);
3023 return;
3024 } else if ("-a".equals(arg)) {
3025 // Ignore, we always dump all.
3026 } else if ("--proto".equals(arg)) {
3027 proto = true;
3028 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
3029 pw.println("Unknown option: " + arg);
3030 return;
3031 } else {
3032 break;
3033 }
3034 opti++;
3035 }
3036 if (opti < args.length) {
3037 String pkg = args[opti];
3038 try {
3039 filterUid = getContext().getPackageManager().getPackageUid(pkg,
3040 PackageManager.MATCH_ANY_USER);
3041 } catch (NameNotFoundException ignored) {
3042 pw.println("Invalid package: " + pkg);
3043 return;
3044 }
3045 }
3046 }
3047
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003048 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07003049 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003050 if (proto) {
3051 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
3052 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003053 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
3054 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003055 }
Christopher Tate7060b042014-06-09 19:50:00 -07003056 } finally {
3057 Binder.restoreCallingIdentity(identityToken);
3058 }
3059 }
Christopher Tate5d346052016-03-08 12:56:08 -08003060
3061 @Override
3062 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07003063 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08003064 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07003065 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08003066 }
Serik Beketayev75915d12018-08-01 16:56:59 -07003067
3068 /**
3069 * <b>For internal system user only!</b>
3070 * Returns a list of all currently-executing jobs.
3071 */
3072 @Override
3073 public List<JobInfo> getStartedJobs() {
3074 final int uid = Binder.getCallingUid();
3075 if (uid != Process.SYSTEM_UID) {
3076 throw new SecurityException(
3077 "getStartedJobs() is system internal use only.");
3078 }
3079
3080 final ArrayList<JobInfo> runningJobs;
3081
3082 synchronized (mLock) {
3083 runningJobs = new ArrayList<>(mActiveServices.size());
3084 for (JobServiceContext jsc : mActiveServices) {
3085 final JobStatus job = jsc.getRunningJobLocked();
3086 if (job != null) {
3087 runningJobs.add(job.getJob());
3088 }
3089 }
3090 }
3091
3092 return runningJobs;
3093 }
3094
3095 /**
3096 * <b>For internal system user only!</b>
3097 * Returns a snapshot of the state of all jobs known to the system.
3098 *
3099 * <p class="note">This is a slow operation, so it should be called sparingly.
3100 */
3101 @Override
3102 public List<JobSnapshot> getAllJobSnapshots() {
3103 final int uid = Binder.getCallingUid();
3104 if (uid != Process.SYSTEM_UID) {
3105 throw new SecurityException(
3106 "getAllJobSnapshots() is system internal use only.");
3107 }
3108 synchronized (mLock) {
3109 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
3110 mJobs.forEachJob((job) -> snapshots.add(
3111 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
3112 isReadyToBeExecutedLocked(job))));
3113 return snapshots;
3114 }
3115 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00003116 };
3117
Christopher Tate5d346052016-03-08 12:56:08 -08003118 // Shell command infrastructure: run the given job immediately
3119 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
3120 if (DEBUG) {
3121 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
3122 + " " + jobId + " f=" + force);
3123 }
3124
3125 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003126 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3127 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08003128 if (uid < 0) {
3129 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3130 }
3131
3132 synchronized (mLock) {
3133 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3134 if (js == null) {
3135 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3136 }
3137
3138 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
3139 if (!js.isConstraintsSatisfied()) {
3140 js.overrideState = 0;
3141 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
3142 }
3143
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003144 queueReadyJobsForExecutionLocked();
3145 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08003146 }
3147 } catch (RemoteException e) {
3148 // can't happen
3149 }
3150 return 0;
3151 }
3152
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003153 // Shell command infrastructure: immediately timeout currently executing jobs
3154 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
3155 boolean hasJobId, int jobId) {
3156 if (DEBUG) {
3157 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
3158 }
3159
3160 synchronized (mLock) {
3161 boolean foundSome = false;
3162 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07003163 final JobServiceContext jc = mActiveServices.get(i);
3164 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08003165 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07003166 foundSome = true;
3167 pw.print("Timing out: ");
3168 js.printUniqueId(pw);
3169 pw.print(" ");
3170 pw.println(js.getServiceComponent().flattenToShortString());
3171 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07003172 }
3173 if (!foundSome) {
3174 pw.println("No matching executing jobs found.");
3175 }
3176 }
3177 return 0;
3178 }
3179
Christopher Tate8c67d122017-09-29 16:54:26 -07003180 // Shell command infrastructure: cancel a scheduled job
3181 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
3182 boolean hasJobId, int jobId) {
3183 if (DEBUG) {
3184 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
3185 }
3186
3187 int pkgUid = -1;
3188 try {
3189 IPackageManager pm = AppGlobals.getPackageManager();
3190 pkgUid = pm.getPackageUid(pkgName, 0, userId);
3191 } catch (RemoteException e) { /* can't happen */ }
3192
3193 if (pkgUid < 0) {
3194 pw.println("Package " + pkgName + " not found.");
3195 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3196 }
3197
3198 if (!hasJobId) {
3199 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
3200 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
3201 pw.println("No matching jobs found.");
3202 }
3203 } else {
3204 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08003205 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07003206 pw.println("No matching job found.");
3207 }
3208 }
3209
3210 return 0;
3211 }
3212
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08003213 void setMonitorBattery(boolean enabled) {
3214 synchronized (mLock) {
3215 if (mBatteryController != null) {
3216 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
3217 }
3218 }
3219 }
3220
3221 int getBatterySeq() {
3222 synchronized (mLock) {
3223 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
3224 }
3225 }
3226
3227 boolean getBatteryCharging() {
3228 synchronized (mLock) {
3229 return mBatteryController != null
3230 ? mBatteryController.getTracker().isOnStablePower() : false;
3231 }
3232 }
3233
3234 boolean getBatteryNotLow() {
3235 synchronized (mLock) {
3236 return mBatteryController != null
3237 ? mBatteryController.getTracker().isBatteryNotLow() : false;
3238 }
3239 }
3240
Dianne Hackborn532ea262017-03-17 17:50:55 -07003241 int getStorageSeq() {
3242 synchronized (mLock) {
3243 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
3244 }
3245 }
3246
3247 boolean getStorageNotLow() {
3248 synchronized (mLock) {
3249 return mStorageController != null
3250 ? mStorageController.getTracker().isStorageNotLow() : false;
3251 }
3252 }
3253
Christopher Tatea732f012017-10-26 17:26:53 -07003254 long getCurrentHeartbeat() {
3255 synchronized (mLock) {
3256 return mHeartbeat;
3257 }
3258 }
3259
Christopher Tated1aebb32018-01-31 13:24:14 -08003260 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07003261 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
3262 try {
3263 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3264 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
3265 if (uid < 0) {
3266 pw.print("unknown("); pw.print(pkgName); pw.println(")");
3267 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3268 }
3269
3270 synchronized (mLock) {
3271 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3272 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
3273 if (js == null) {
3274 pw.print("unknown("); UserHandle.formatUid(pw, uid);
3275 pw.print("/jid"); pw.print(jobId); pw.println(")");
3276 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3277 }
3278
3279 boolean printed = false;
3280 if (mPendingJobs.contains(js)) {
3281 pw.print("pending");
3282 printed = true;
3283 }
3284 if (isCurrentlyActiveLocked(js)) {
3285 if (printed) {
3286 pw.print(" ");
3287 }
3288 printed = true;
3289 pw.println("active");
3290 }
3291 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
3292 if (printed) {
3293 pw.print(" ");
3294 }
3295 printed = true;
3296 pw.println("user-stopped");
3297 }
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003298 if (!ArrayUtils.contains(mStartedUsers, js.getSourceUserId())) {
3299 if (printed) {
3300 pw.print(" ");
3301 }
3302 printed = true;
3303 pw.println("source-user-stopped");
3304 }
Dianne Hackborn6d068262017-05-16 13:14:37 -07003305 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
3306 if (printed) {
3307 pw.print(" ");
3308 }
3309 printed = true;
3310 pw.println("backing-up");
3311 }
3312 boolean componentPresent = false;
3313 try {
3314 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3315 js.getServiceComponent(),
3316 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3317 js.getUserId()) != null);
3318 } catch (RemoteException e) {
3319 }
3320 if (!componentPresent) {
3321 if (printed) {
3322 pw.print(" ");
3323 }
3324 printed = true;
3325 pw.println("no-component");
3326 }
3327 if (js.isReady()) {
3328 if (printed) {
3329 pw.print(" ");
3330 }
3331 printed = true;
3332 pw.println("ready");
3333 }
3334 if (!printed) {
3335 pw.print("waiting");
3336 }
3337 pw.println();
3338 }
3339 } catch (RemoteException e) {
3340 // can't happen
3341 }
3342 return 0;
3343 }
3344
Christopher Tated1aebb32018-01-31 13:24:14 -08003345 // Shell command infrastructure
3346 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
3347 if (numBeats < 1) {
3348 pw.println(getCurrentHeartbeat());
3349 return 0;
3350 }
3351
3352 pw.print("Advancing standby heartbeat by ");
3353 pw.println(numBeats);
3354 synchronized (mLock) {
3355 advanceHeartbeatLocked(numBeats);
3356 }
3357 return 0;
3358 }
3359
lpeter318abc92018-05-04 16:13:14 +08003360 void triggerDockState(boolean idleState) {
3361 final Intent dockIntent;
3362 if (idleState) {
3363 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3364 } else {
3365 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3366 }
3367 dockIntent.setPackage("android");
3368 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3369 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3370 }
3371
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003372 static void dumpHelp(PrintWriter pw) {
3373 pw.println("Job Scheduler (jobscheduler) dump options:");
3374 pw.println(" [-h] [package] ...");
3375 pw.println(" -h: print this help");
3376 pw.println(" [package] is an optional package name to limit the output to.");
3377 }
3378
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003379 /** Sort jobs by caller UID, then by Job ID. */
3380 private static void sortJobs(List<JobStatus> jobs) {
3381 Collections.sort(jobs, new Comparator<JobStatus>() {
3382 @Override
3383 public int compare(JobStatus o1, JobStatus o2) {
3384 int uid1 = o1.getUid();
3385 int uid2 = o2.getUid();
3386 int id1 = o1.getJobId();
3387 int id2 = o2.getJobId();
3388 if (uid1 != uid2) {
3389 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003390 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003391 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003392 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003393 });
3394 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003395
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003396 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003397 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003398 final long nowElapsed = sElapsedRealtimeClock.millis();
3399 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003400 final Predicate<JobStatus> predicate = (js) -> {
3401 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3402 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3403 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003404 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003405 mConstants.dump(pw);
3406 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003407
3408 pw.println(" Heartbeat:");
3409 pw.print(" Current: "); pw.println(mHeartbeat);
3410 pw.println(" Next");
3411 pw.print(" ACTIVE: "); pw.println(mNextBucketHeartbeat[0]);
3412 pw.print(" WORKING: "); pw.println(mNextBucketHeartbeat[1]);
3413 pw.print(" FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3414 pw.print(" RARE: "); pw.println(mNextBucketHeartbeat[3]);
3415 pw.print(" Last heartbeat: ");
3416 TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3417 pw.println();
3418 pw.print(" Next heartbeat: ");
3419 TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3420 nowElapsed, pw);
3421 pw.println();
Makoto Onuki0525b982018-05-02 14:46:59 -07003422 pw.print(" In parole?: ");
3423 pw.print(mInParole);
3424 pw.println();
Wei Wang8c0c3c12018-11-14 14:56:52 -08003425 pw.print(" In thermal throttling?: ");
3426 pw.print(mThermalConstraint);
3427 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003428 pw.println();
3429
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003430 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003431 pw.print("Registered ");
3432 pw.print(mJobs.size());
3433 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003434 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003435 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003436 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003437 for (JobStatus job : jobs) {
3438 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3439 pw.println(job.toShortStringExceptUniqueId());
3440
3441 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003442 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003443 continue;
3444 }
3445
Dianne Hackborn6d068262017-05-16 13:14:37 -07003446 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003447 pw.print(" Last run heartbeat: ");
3448 pw.print(heartbeatWhenJobsLastRun(job));
3449 pw.println();
3450
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003451 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003452 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003453 pw.print(" (job=");
3454 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003455 pw.print(" user=");
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003456 pw.print(areUsersStartedLocked(job));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003457 pw.print(" !pending=");
3458 pw.print(!mPendingJobs.contains(job));
3459 pw.print(" !active=");
3460 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003461 pw.print(" !backingup=");
3462 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003463 pw.print(" comp=");
3464 boolean componentPresent = false;
3465 try {
3466 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3467 job.getServiceComponent(),
3468 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3469 job.getUserId()) != null);
3470 } catch (RemoteException e) {
3471 }
3472 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003473 pw.println(")");
3474 }
Christopher Tate7060b042014-06-09 19:50:00 -07003475 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003476 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003477 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003478 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003479 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003480 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3481 pw.increaseIndent();
3482 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3483 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003484 }
3485 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003486 pw.println("Uid priority overrides:");
3487 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003488 int uid = mUidPriorityOverride.keyAt(i);
3489 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3490 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3491 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3492 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003493 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003494 if (mBackingUpUids.size() > 0) {
3495 pw.println();
3496 pw.println("Backing up uids:");
3497 boolean first = true;
3498 for (int i = 0; i < mBackingUpUids.size(); i++) {
3499 int uid = mBackingUpUids.keyAt(i);
3500 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3501 if (first) {
3502 pw.print(" ");
3503 first = false;
3504 } else {
3505 pw.print(", ");
3506 }
3507 pw.print(UserHandle.formatUid(uid));
3508 }
3509 }
3510 pw.println();
3511 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003512 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003513 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003514 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003515 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3516 pw.println();
3517 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003518 pw.println("Pending queue:");
3519 for (int i=0; i<mPendingJobs.size(); i++) {
3520 JobStatus job = mPendingJobs.get(i);
3521 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3522 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003523 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003524 int priority = evaluateJobPriorityLocked(job);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003525 pw.print(" Evaluated priority: ");
3526 pw.println(JobInfo.getPriorityString(priority));
3527
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003528 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003529 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003530 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003531 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003532 }
Christopher Tate7060b042014-06-09 19:50:00 -07003533 pw.println();
3534 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003535 for (int i=0; i<mActiveServices.size(); i++) {
3536 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003537 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003538 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003539 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003540 if (jsc.mStoppedReason != null) {
3541 pw.print("inactive since ");
3542 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3543 pw.print(", stopped because: ");
3544 pw.println(jsc.mStoppedReason);
3545 } else {
3546 pw.println("inactive");
3547 }
Christopher Tate7060b042014-06-09 19:50:00 -07003548 continue;
3549 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003550 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003551 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003552 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003553 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003554 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003555 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003556 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003557 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003558 pw.print(" Evaluated priority: ");
3559 pw.println(JobInfo.getPriorityString(priority));
3560
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003561 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003562 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003563 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003564 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3565 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003566 }
3567 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003568 if (filterUid == -1) {
3569 pw.println();
3570 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3571 pw.print("mReportedActive="); pw.println(mReportedActive);
3572 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3573 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003574 pw.println();
3575 pw.print("PersistStats: ");
3576 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003577 }
3578 pw.println();
3579 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003580
3581 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3582 ProtoOutputStream proto = new ProtoOutputStream(fd);
3583 final int filterUidFinal = UserHandle.getAppId(filterUid);
3584 final long nowElapsed = sElapsedRealtimeClock.millis();
3585 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003586 final Predicate<JobStatus> predicate = (js) -> {
3587 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3588 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3589 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003590
3591 synchronized (mLock) {
3592 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003593 proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3594 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3595 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3596 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3597 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3598 proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3599 mLastHeartbeatTime - nowUptime);
3600 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3601 mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
Makoto Onuki0525b982018-05-02 14:46:59 -07003602 proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
Wei Wang8c0c3c12018-11-14 14:56:52 -08003603 proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003604
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003605 for (int u : mStartedUsers) {
3606 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3607 }
3608 if (mJobs.size() > 0) {
3609 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3610 sortJobs(jobs);
3611 for (JobStatus job : jobs) {
3612 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3613 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3614
3615 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003616 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003617 continue;
3618 }
3619
3620 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3621
3622 // isReadyToBeExecuted
3623 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3624 job.isReady());
3625 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
Kweku Adams8bd5edc2018-12-07 18:33:39 -08003626 areUsersStartedLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003627 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3628 mPendingJobs.contains(job));
3629 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3630 isCurrentlyActiveLocked(job));
3631 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3632 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3633 boolean componentPresent = false;
3634 try {
3635 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3636 job.getServiceComponent(),
3637 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3638 job.getUserId()) != null);
3639 } catch (RemoteException e) {
3640 }
3641 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3642 componentPresent);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003643 proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003644
3645 proto.end(rjToken);
3646 }
3647 }
3648 for (StateController controller : mControllers) {
3649 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003650 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003651 }
3652 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3653 int uid = mUidPriorityOverride.keyAt(i);
3654 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3655 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3656 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3657 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3658 mUidPriorityOverride.valueAt(i));
3659 proto.end(pToken);
3660 }
3661 }
3662 for (int i = 0; i < mBackingUpUids.size(); i++) {
3663 int uid = mBackingUpUids.keyAt(i);
3664 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3665 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3666 }
3667 }
3668
3669 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3670 filterUidFinal);
3671 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3672 filterUidFinal);
3673
3674 for (JobStatus job : mPendingJobs) {
3675 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3676
3677 job.writeToShortProto(proto, PendingJob.INFO);
3678 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003679 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003680 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3681
3682 proto.end(pjToken);
3683 }
3684 for (JobServiceContext jsc : mActiveServices) {
3685 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3686 final JobStatus job = jsc.getRunningJobLocked();
3687
3688 if (job == null) {
3689 final long ijToken = proto.start(ActiveJob.INACTIVE);
3690
3691 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3692 nowElapsed - jsc.mStoppedTime);
3693 if (jsc.mStoppedReason != null) {
3694 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3695 jsc.mStoppedReason);
3696 }
3697
3698 proto.end(ijToken);
3699 } else {
3700 final long rjToken = proto.start(ActiveJob.RUNNING);
3701
3702 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3703
3704 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3705 nowElapsed - jsc.getExecutionStartTimeElapsed());
3706 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3707 jsc.getTimeoutElapsed() - nowElapsed);
3708
3709 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3710
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003711 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
3712 evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003713
3714 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3715 nowUptime - job.madeActive);
3716 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3717 job.madeActive - job.madePending);
3718
3719 proto.end(rjToken);
3720 }
3721 proto.end(ajToken);
3722 }
3723 if (filterUid == -1) {
3724 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3725 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3726 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3727 }
3728 }
3729
3730 proto.flush();
3731 }
Christopher Tate7060b042014-06-09 19:50:00 -07003732}