blob: ba6857d9f468562e5e0fdf640476f6af31790888 [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;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070070import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080071import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070072import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070073import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070074import android.util.Slog;
75import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080076import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080077import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080078import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080079import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080080
Wei Wang8c0c3c12018-11-14 14:56:52 -080081import com.android.internal.annotations.GuardedBy;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070082import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070083import com.android.internal.app.IBatteryStats;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070084import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060085import com.android.internal.util.DumpUtils;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -070086import com.android.internal.util.IndentingPrintWriter;
Makoto Onuki15407842018-01-19 14:23:11 -080087import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070088import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080089import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070090import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080091import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080092import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
93import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Makoto Onukib5d5e972018-02-20 14:44:20 -080094import com.android.server.job.JobSchedulerServiceDumpProto.RegisteredJob;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070095import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070096import com.android.server.job.controllers.BatteryController;
97import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080098import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070099import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -0700100import com.android.server.job.controllers.IdleController;
101import com.android.server.job.controllers.JobStatus;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800102import com.android.server.job.controllers.QuotaController;
Christopher Tate7060b042014-06-09 19:50:00 -0700103import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700104import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700105import com.android.server.job.controllers.TimeController;
106
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700107import libcore.util.EmptyArray;
108
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700109import java.io.FileDescriptor;
110import java.io.PrintWriter;
111import java.time.Clock;
112import java.util.ArrayList;
113import java.util.Arrays;
114import java.util.Collections;
115import java.util.Comparator;
Christopher Tate325768c2018-03-07 16:07:56 -0800116import java.util.HashMap;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700117import java.util.List;
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -0700118import java.util.function.Consumer;
Makoto Onuki15407842018-01-19 14:23:11 -0800119import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700120
Christopher Tate7060b042014-06-09 19:50:00 -0700121/**
122 * Responsible for taking jobs representing work to be performed by a client app, and determining
123 * based on the criteria specified when that job should be run against the client application's
124 * endpoint.
125 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
126 * about constraints, or the state of active jobs. It receives callbacks from the various
127 * controllers and completed jobs and operates accordingly.
128 *
129 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
130 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
131 * @hide
132 */
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600133public class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700134 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700135 public static final String TAG = "JobScheduler";
136 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700137 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800138
Dianne Hackborn970510b2016-02-24 16:56:42 -0800139 /** The maximum number of concurrent jobs we run at one time. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800140 static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800141 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800142 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800143 /** The maximum number of jobs that we allow an unprivileged app to schedule */
144 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700145
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700146 @VisibleForTesting
147 public static Clock sSystemClock = Clock.systemUTC();
148 @VisibleForTesting
149 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
150 @VisibleForTesting
151 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800152
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800153 /** Global local for all job scheduler state. */
154 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700155 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700156 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700157 /** Tracking the standby bucket state of each app */
158 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700159 /** Tracking amount of time each package runs for. */
160 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Makoto Onuki714f97d2018-12-05 11:18:13 -0800161 final JobConcurrencyManager mConcurrencyManager;
Christopher Tate7060b042014-06-09 19:50:00 -0700162
163 static final int MSG_JOB_EXPIRED = 0;
164 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700165 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000166 static final int MSG_CHECK_JOB_GREEDY = 3;
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800167 static final int MSG_UID_STATE_CHANGED = 4;
168 static final int MSG_UID_GONE = 5;
169 static final int MSG_UID_ACTIVE = 6;
170 static final int MSG_UID_IDLE = 7;
Christopher Tate7060b042014-06-09 19:50:00 -0700171
Christopher Tate7060b042014-06-09 19:50:00 -0700172 /**
173 * Track Services that have currently active or pending jobs. The index is provided by
174 * {@link JobStatus#getServiceToken()}
175 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700176 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700177
Christopher Tate7060b042014-06-09 19:50:00 -0700178 /** List of controllers that will notify this service of updates to jobs. */
Makoto Onuki714f97d2018-12-05 11:18:13 -0800179 final List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800180 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700181 private final BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700182 /** Need direct access to this for testing. */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700183 private final StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700184 /** Need directly for sending uid state changes */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700185 private final DeviceIdleJobsController mDeviceIdleJobsController;
Wei Wang8c0c3c12018-11-14 14:56:52 -0800186 /** Need directly for receiving thermal events */
187 private IThermalService mThermalService;
188 /** Thermal constraint. */
189 @GuardedBy("mLock")
190 private boolean mThermalConstraint = false;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700191
Christopher Tate7060b042014-06-09 19:50:00 -0700192 /**
193 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
194 * when ready to execute them.
195 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700196 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700197
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700198 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700199
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700200 final JobHandler mHandler;
201 final JobSchedulerStub mJobSchedulerStub;
202
Christopher Tatea732f012017-10-26 17:26:53 -0700203 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800204 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700205 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800206 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800207 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800208 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700209
210 /**
211 * Set to true once we are allowed to run third party apps.
212 */
213 boolean mReadyToRock;
214
Christopher Tate7060b042014-06-09 19:50:00 -0700215 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800216 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800217 */
218 boolean mReportedActive;
219
220 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800221 * Are we currently in device-wide standby parole?
222 */
223 volatile boolean mInParole;
224
225 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800226 * Current limit on the number of concurrent JobServiceContext entries we want to
227 * keep actively running a job.
228 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700229 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800230
231 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800232 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800233 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800234 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
235
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700236 /**
237 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
238 */
239 final SparseIntArray mBackingUpUids = new SparseIntArray();
240
Christopher Tatea732f012017-10-26 17:26:53 -0700241 /**
242 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
243 * next become runnable. Index into this array is by normalized bucket:
244 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
245 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
246 * and NEVER apps don't get them at all.
247 */
248 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
249 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800250 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
251
Christopher Tate325768c2018-03-07 16:07:56 -0800252 /**
253 * Named indices into the STANDBY_BEATS array, for clarity in referring to
254 * specific buckets' bookkeeping.
255 */
Kweku Adams4836f9d2018-11-12 17:04:17 -0800256 public static final int ACTIVE_INDEX = 0;
257 public static final int WORKING_INDEX = 1;
258 public static final int FREQUENT_INDEX = 2;
259 public static final int RARE_INDEX = 3;
260 public static final int NEVER_INDEX = 4;
Christopher Tate325768c2018-03-07 16:07:56 -0800261
262 /**
263 * Bookkeeping about when jobs last run. We keep our own record in heartbeat time,
264 * rather than rely on Usage Stats' timestamps, because heartbeat time can be
265 * manipulated for testing purposes and we need job runnability to track that rather
266 * than real time.
267 *
268 * Outer SparseArray slices by user handle; inner map of package name to heartbeat
269 * is a HashMap<> rather than ArrayMap<> because we expect O(hundreds) of keys
270 * and it will be accessed in a known-hot code path.
271 */
272 final SparseArray<HashMap<String, Long>> mLastJobHeartbeats = new SparseArray<>();
273
Christopher Tated1aebb32018-01-31 13:24:14 -0800274 static final String HEARTBEAT_TAG = "*job.heartbeat*";
275 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700276
Dianne Hackborn970510b2016-02-24 16:56:42 -0800277 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
278
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700279 private class ConstantsObserver extends ContentObserver {
280 private ContentResolver mResolver;
281
282 public ConstantsObserver(Handler handler) {
283 super(handler);
284 }
285
286 public void start(ContentResolver resolver) {
287 mResolver = resolver;
288 mResolver.registerContentObserver(Settings.Global.getUriFor(
289 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
290 updateConstants();
291 }
292
293 @Override
294 public void onChange(boolean selfChange, Uri uri) {
295 updateConstants();
296 }
297
298 private void updateConstants() {
299 synchronized (mLock) {
300 try {
301 mConstants.updateConstantsLocked(Settings.Global.getString(mResolver,
302 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Kweku Adams4836f9d2018-11-12 17:04:17 -0800303 for (int controller = 0; controller < mControllers.size(); controller++) {
304 final StateController sc = mControllers.get(controller);
305 sc.onConstantsUpdatedLocked();
306 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700307 } catch (IllegalArgumentException e) {
308 // Failed to parse the settings string, log this and move on
309 // with defaults.
310 Slog.e(TAG, "Bad jobscheduler settings", e);
311 }
312 }
313
Kweku Adams4836f9d2018-11-12 17:04:17 -0800314 if (mConstants.USE_HEARTBEATS) {
315 // Reset the heartbeat alarm based on the new heartbeat duration
316 setNextHeartbeatAlarm();
317 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700318 }
319 }
320
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800321 /**
Wei Wang8c0c3c12018-11-14 14:56:52 -0800322 * Thermal event received from Thermal Service
323 */
324 private final class ThermalStatusListener extends IThermalStatusListener.Stub {
325 @Override public void onStatusChange(int status) {
326 // Throttle for Temperature.THROTTLING_SEVERE and above
327 synchronized (mLock) {
328 mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
329 }
330 onControllerStateChanged();
331 }
332 }
333
334 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700335 * All times are in milliseconds. These constants are kept synchronized with the system
336 * global Settings. Any access to this class or its fields should be done while
337 * holding the JobSchedulerService.mLock lock.
338 */
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700339 public static class Constants {
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700340 // Key names stored in the settings value.
341 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
342 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800343 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700344 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700345 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
346 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
347 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
348 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
349 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
350 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
351 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
352 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
353 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
354 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700355 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
356 = "max_standard_reschedule_count";
357 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
358 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
359 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700360 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
361 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
362 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
363 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700364 private static final String KEY_CONN_CONGESTION_DELAY_FRAC = "conn_congestion_delay_frac";
365 private static final String KEY_CONN_PREFETCH_RELAX_FRAC = "conn_prefetch_relax_frac";
Kweku Adams4836f9d2018-11-12 17:04:17 -0800366 private static final String KEY_USE_HEARTBEATS = "use_heartbeats";
367 private static final String KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
368 "qc_allowed_time_per_period_ms";
369 private static final String KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
370 "qc_in_quota_buffer_ms";
371 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
372 "qc_window_size_active_ms";
373 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
374 "qc_window_size_working_ms";
375 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
376 "qc_window_size_frequent_ms";
377 private static final String KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
378 "qc_window_size_rare_ms";
Kweku Adams045fb5722018-12-11 14:29:10 -0800379 private static final String KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
380 "qc_max_execution_time_ms";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700381
382 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
383 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800384 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700385 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700386 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
387 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
388 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
389 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
390 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
391 private static final int DEFAULT_FG_JOB_COUNT = 4;
392 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
393 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700394 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700395 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700396 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
397 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
398 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
399 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700400 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000401 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
402 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700403 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700404 private static final float DEFAULT_CONN_CONGESTION_DELAY_FRAC = 0.5f;
405 private static final float DEFAULT_CONN_PREFETCH_RELAX_FRAC = 0.5f;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800406 private static final boolean DEFAULT_USE_HEARTBEATS = true;
407 private static final long DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
408 10 * 60 * 1000L; // 10 minutes
409 private static final long DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
410 30 * 1000L; // 30 seconds
411 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
412 10 * 60 * 1000L; // 10 minutes for ACTIVE -- ACTIVE apps can run jobs at any time
413 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
414 2 * 60 * 60 * 1000L; // 2 hours
415 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
416 8 * 60 * 60 * 1000L; // 8 hours
417 private static final long DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
418 24 * 60 * 60 * 1000L; // 24 hours
Kweku Adams045fb5722018-12-11 14:29:10 -0800419 private static final long DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
420 4 * 60 * 60 * 1000L; // 4 hours
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700421
422 /**
423 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
424 * early.
425 */
426 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
427 /**
428 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
429 * things early.
430 */
431 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
432 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800433 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
434 * schedule things early.
435 */
436 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
437 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700438 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
439 * schedule things early.
440 */
441 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
442 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700443 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
444 * things early. 1 == Run connectivity jobs as soon as ready.
445 */
446 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
447 /**
448 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
449 * schedule things early.
450 */
451 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
452 /**
453 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
454 * running some work early. This (and thus the other min counts) is now set to 1, to
455 * prevent any batching at this level. Since we now do batching through doze, that is
456 * a much better mechanism.
457 */
458 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
459 /**
460 * This is the job execution factor that is considered to be heavy use of the system.
461 */
462 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
463 /**
464 * This is the job execution factor that is considered to be moderate use of the system.
465 */
466 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
467 /**
468 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
469 */
470 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
471 /**
472 * The maximum number of background jobs we allow when the system is in a normal
473 * memory state.
474 */
475 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
476 /**
477 * The maximum number of background jobs we allow when the system is in a moderate
478 * memory state.
479 */
480 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
481 /**
482 * The maximum number of background jobs we allow when the system is in a low
483 * memory state.
484 */
485 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
486 /**
487 * The maximum number of background jobs we allow when the system is in a critical
488 * memory state.
489 */
490 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700491 /**
492 * The maximum number of times we allow a job to have itself rescheduled before
493 * giving up on it, for standard jobs.
494 */
495 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
496 /**
497 * The maximum number of times we allow a job to have itself rescheduled before
498 * giving up on it, for jobs that are executing work.
499 */
500 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
501 /**
502 * The minimum backoff time to allow for linear backoff.
503 */
504 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
505 /**
506 * The minimum backoff time to allow for exponential backoff.
507 */
508 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700509 /**
510 * How often we recalculate runnability based on apps' standby bucket assignment.
511 * This should be prime relative to common time interval lengths such as a quarter-
512 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
513 */
514 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700515 /**
516 * Mapping: standby bucket -> number of heartbeats between each sweep of that
517 * bucket's jobs.
518 *
519 * Bucket assignments as recorded in the JobStatus objects are normalized to be
520 * indices into this array, rather than the raw constants used
521 * by AppIdleHistory.
522 */
523 final int[] STANDBY_BEATS = {
524 0,
525 DEFAULT_STANDBY_WORKING_BEATS,
526 DEFAULT_STANDBY_FREQUENT_BEATS,
527 DEFAULT_STANDBY_RARE_BEATS
528 };
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700529 /**
530 * The fraction of a job's running window that must pass before we
531 * consider running it when the network is congested.
532 */
533 public float CONN_CONGESTION_DELAY_FRAC = DEFAULT_CONN_CONGESTION_DELAY_FRAC;
534 /**
535 * The fraction of a prefetch job's running window that must pass before
536 * we consider matching it against a metered network.
537 */
538 public float CONN_PREFETCH_RELAX_FRAC = DEFAULT_CONN_PREFETCH_RELAX_FRAC;
Kweku Adams4836f9d2018-11-12 17:04:17 -0800539 /**
540 * Whether to use heartbeats or rolling window for quota management. True will use
541 * heartbeats, false will use a rolling window.
542 */
543 public boolean USE_HEARTBEATS = DEFAULT_USE_HEARTBEATS;
544
545 /** How much time each app will have to run jobs within their standby bucket window. */
546 public long QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS =
547 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS;
548
549 /**
550 * How much time the package should have before transitioning from out-of-quota to in-quota.
551 * This should not affect processing if the package is already in-quota.
552 */
553 public long QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS =
554 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS;
555
556 /**
557 * The quota window size of the particular standby bucket. Apps in this standby bucket are
558 * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
559 * WINDOW_SIZE_MS.
560 */
561 public long QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS =
562 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS;
563
564 /**
565 * The quota window size of the particular standby bucket. Apps in this standby bucket are
566 * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
567 * WINDOW_SIZE_MS.
568 */
569 public long QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS =
570 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS;
571
572 /**
573 * The quota window size of the particular standby bucket. Apps in this standby bucket are
574 * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
575 * WINDOW_SIZE_MS.
576 */
577 public long QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS =
578 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS;
579
580 /**
581 * The quota window size of the particular standby bucket. Apps in this standby bucket are
582 * expected to run only {@link QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS} within the past
583 * WINDOW_SIZE_MS.
584 */
585 public long QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS =
586 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700587
Kweku Adams045fb5722018-12-11 14:29:10 -0800588 /**
589 * The maximum amount of time an app can have its jobs running within a 24 hour window.
590 */
591 public long QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS =
592 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS;
593
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700594 private final KeyValueListParser mParser = new KeyValueListParser(',');
595
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700596 void updateConstantsLocked(String value) {
597 try {
598 mParser.setString(value);
599 } catch (Exception e) {
600 // Failed to parse the settings string, log this and move on
601 // with defaults.
602 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700603 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800604
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700605 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
606 DEFAULT_MIN_IDLE_COUNT);
607 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
608 DEFAULT_MIN_CHARGING_COUNT);
609 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
610 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
611 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
612 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
613 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
614 DEFAULT_MIN_CONNECTIVITY_COUNT);
615 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
616 DEFAULT_MIN_CONTENT_COUNT);
617 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
618 DEFAULT_MIN_READY_JOBS_COUNT);
619 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
620 DEFAULT_HEAVY_USE_FACTOR);
621 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
622 DEFAULT_MODERATE_USE_FACTOR);
623 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
624 DEFAULT_FG_JOB_COUNT);
625 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
626 DEFAULT_BG_NORMAL_JOB_COUNT);
627 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
628 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
629 }
630 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
631 DEFAULT_BG_MODERATE_JOB_COUNT);
632 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
633 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
634 }
635 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
636 DEFAULT_BG_LOW_JOB_COUNT);
637 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
638 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
639 }
640 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
641 DEFAULT_BG_CRITICAL_JOB_COUNT);
642 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
643 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
644 }
645 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
646 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
647 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
648 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
649 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
650 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
651 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
652 DEFAULT_MIN_EXP_BACKOFF_TIME);
653 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
654 DEFAULT_STANDBY_HEARTBEAT_TIME);
Christopher Tate325768c2018-03-07 16:07:56 -0800655 STANDBY_BEATS[WORKING_INDEX] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700656 DEFAULT_STANDBY_WORKING_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800657 STANDBY_BEATS[FREQUENT_INDEX] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700658 DEFAULT_STANDBY_FREQUENT_BEATS);
Christopher Tate325768c2018-03-07 16:07:56 -0800659 STANDBY_BEATS[RARE_INDEX] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700660 DEFAULT_STANDBY_RARE_BEATS);
661 CONN_CONGESTION_DELAY_FRAC = mParser.getFloat(KEY_CONN_CONGESTION_DELAY_FRAC,
662 DEFAULT_CONN_CONGESTION_DELAY_FRAC);
663 CONN_PREFETCH_RELAX_FRAC = mParser.getFloat(KEY_CONN_PREFETCH_RELAX_FRAC,
664 DEFAULT_CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800665 USE_HEARTBEATS = mParser.getBoolean(KEY_USE_HEARTBEATS, DEFAULT_USE_HEARTBEATS);
666 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS = mParser.getDurationMillis(
667 KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
668 DEFAULT_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
669 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS = mParser.getDurationMillis(
670 KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
671 DEFAULT_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
672 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS = mParser.getDurationMillis(
673 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
674 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
675 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS = mParser.getDurationMillis(
676 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
677 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
678 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS = mParser.getDurationMillis(
679 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
680 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
681 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS = mParser.getDurationMillis(
682 KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
683 DEFAULT_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800684 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS = mParser.getDurationMillis(
685 KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
686 DEFAULT_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700687 }
688
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700689 void dump(IndentingPrintWriter pw) {
690 pw.println("Settings:");
691 pw.increaseIndent();
692 pw.printPair(KEY_MIN_IDLE_COUNT, MIN_IDLE_COUNT).println();
693 pw.printPair(KEY_MIN_CHARGING_COUNT, MIN_CHARGING_COUNT).println();
694 pw.printPair(KEY_MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT).println();
695 pw.printPair(KEY_MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT).println();
696 pw.printPair(KEY_MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT).println();
697 pw.printPair(KEY_MIN_CONTENT_COUNT, MIN_CONTENT_COUNT).println();
698 pw.printPair(KEY_MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT).println();
699 pw.printPair(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
700 pw.printPair(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
701 pw.printPair(KEY_FG_JOB_COUNT, FG_JOB_COUNT).println();
702 pw.printPair(KEY_BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT).println();
703 pw.printPair(KEY_BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT).println();
704 pw.printPair(KEY_BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT).println();
705 pw.printPair(KEY_BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT).println();
706 pw.printPair(KEY_MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT).println();
707 pw.printPair(KEY_MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT).println();
708 pw.printPair(KEY_MIN_LINEAR_BACKOFF_TIME, MIN_LINEAR_BACKOFF_TIME).println();
709 pw.printPair(KEY_MIN_EXP_BACKOFF_TIME, MIN_EXP_BACKOFF_TIME).println();
710 pw.printPair(KEY_STANDBY_HEARTBEAT_TIME, STANDBY_HEARTBEAT_TIME).println();
711 pw.print("standby_beats={");
Christopher Tatea732f012017-10-26 17:26:53 -0700712 pw.print(STANDBY_BEATS[0]);
713 for (int i = 1; i < STANDBY_BEATS.length; i++) {
714 pw.print(", ");
715 pw.print(STANDBY_BEATS[i]);
716 }
717 pw.println('}');
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700718 pw.printPair(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
719 pw.printPair(KEY_CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC).println();
Kweku Adams4836f9d2018-11-12 17:04:17 -0800720 pw.printPair(KEY_USE_HEARTBEATS, USE_HEARTBEATS).println();
721 pw.printPair(KEY_QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS,
722 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS).println();
723 pw.printPair(KEY_QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS,
724 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS).println();
725 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS,
726 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS).println();
727 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS,
728 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS).println();
729 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS,
730 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS).println();
731 pw.printPair(KEY_QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS,
732 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS).println();
Kweku Adams045fb5722018-12-11 14:29:10 -0800733 pw.printPair(KEY_QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS,
734 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS).println();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700735 pw.decreaseIndent();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700736 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800737
738 void dump(ProtoOutputStream proto, long fieldId) {
739 final long token = proto.start(fieldId);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800740 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
741 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
742 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
743 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
744 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
745 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
746 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
747 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
748 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
749 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
750 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
751 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
752 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
753 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
754 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
755 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
756 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
757 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
758 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800759 for (int period : STANDBY_BEATS) {
760 proto.write(ConstantsProto.STANDBY_BEATS, period);
761 }
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700762 proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
763 proto.write(ConstantsProto.CONN_PREFETCH_RELAX_FRAC, CONN_PREFETCH_RELAX_FRAC);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800764 proto.write(ConstantsProto.USE_HEARTBEATS, USE_HEARTBEATS);
765
766 final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
767 proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
768 QUOTA_CONTROLLER_ALLOWED_TIME_PER_PERIOD_MS);
769 proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS,
770 QUOTA_CONTROLLER_IN_QUOTA_BUFFER_MS);
771 proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
772 QUOTA_CONTROLLER_WINDOW_SIZE_ACTIVE_MS);
773 proto.write(ConstantsProto.QuotaController.WORKING_WINDOW_SIZE_MS,
774 QUOTA_CONTROLLER_WINDOW_SIZE_WORKING_MS);
775 proto.write(ConstantsProto.QuotaController.FREQUENT_WINDOW_SIZE_MS,
776 QUOTA_CONTROLLER_WINDOW_SIZE_FREQUENT_MS);
777 proto.write(ConstantsProto.QuotaController.RARE_WINDOW_SIZE_MS,
778 QUOTA_CONTROLLER_WINDOW_SIZE_RARE_MS);
Kweku Adams045fb5722018-12-11 14:29:10 -0800779 proto.write(ConstantsProto.QuotaController.MAX_EXECUTION_TIME_MS,
780 QUOTA_CONTROLLER_MAX_EXECUTION_TIME_MS);
Kweku Adams4836f9d2018-11-12 17:04:17 -0800781 proto.end(qcToken);
782
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800783 proto.end(token);
784 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700785 }
786
787 final Constants mConstants;
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700788 final ConstantsObserver mConstantsObserver;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700789
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700790 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
791 if (o1.enqueueTime < o2.enqueueTime) {
792 return -1;
793 }
794 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
795 };
796
797 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
798 int where = Collections.binarySearch(array, newItem, comparator);
799 if (where < 0) {
800 where = ~where;
801 }
802 array.add(where, newItem);
803 }
804
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700805 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700806 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
807 * still clean up. On reinstall the package will have a new uid.
808 */
809 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
810 @Override
811 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700812 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700813 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700814 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700815 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700816 final String pkgName = getPackageName(intent);
817 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
818
Christopher Tateee7805b2016-07-15 16:56:56 -0700819 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700820 // Purge the app's jobs if the whole package was just disabled. When this is
821 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700822 if (pkgName != null && pkgUid != -1) {
823 final String[] changedComponents = intent.getStringArrayExtra(
824 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
825 if (changedComponents != null) {
826 for (String component : changedComponents) {
827 if (component.equals(pkgName)) {
828 if (DEBUG) {
829 Slog.d(TAG, "Package state change: " + pkgName);
830 }
831 try {
832 final int userId = UserHandle.getUserId(pkgUid);
833 IPackageManager pm = AppGlobals.getPackageManager();
834 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
835 if (state == COMPONENT_ENABLED_STATE_DISABLED
836 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
837 if (DEBUG) {
838 Slog.d(TAG, "Removing jobs for package " + pkgName
839 + " in user " + userId);
840 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700841 cancelJobsForPackageAndUid(pkgName, pkgUid,
842 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700843 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700844 } catch (RemoteException|IllegalArgumentException e) {
845 /*
846 * IllegalArgumentException means that the package doesn't exist.
847 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
848 * behind outright uninstall, so by the time we try to act it's gone.
849 * We don't need to act on this PACKAGE_CHANGED when this happens;
850 * we'll get a PACKAGE_REMOVED later and clean up then.
851 *
852 * RemoteException can't actually happen; the package manager is
853 * running in this same process.
854 */
855 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700856 break;
857 }
858 }
Kweku Adamscdbfcb92018-12-06 17:05:15 -0800859 if (DEBUG) {
860 Slog.d(TAG, "Something in " + pkgName
861 + " changed. Reevaluating controller states.");
862 }
863 synchronized (mLock) {
864 for (int c = mControllers.size() - 1; c >= 0; --c) {
865 mControllers.get(c).reevaluateStateLocked(pkgUid);
866 }
867 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700868 }
869 } else {
870 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
871 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700872 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700873 // If this is an outright uninstall rather than the first half of an
874 // app update sequence, cancel the jobs associated with the app.
875 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
876 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
877 if (DEBUG) {
878 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
879 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700880 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -0800881 synchronized (mLock) {
882 for (int c = 0; c < mControllers.size(); ++c) {
883 mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
884 }
885 }
Christopher Tate7060b042014-06-09 19:50:00 -0700886 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700887 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700888 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
889 if (DEBUG) {
890 Slog.d(TAG, "Removing jobs for user: " + userId);
891 }
892 cancelJobsForUser(userId);
Kweku Adamsa9f8e1f2018-12-12 16:06:54 -0800893 synchronized (mLock) {
894 for (int c = 0; c < mControllers.size(); ++c) {
895 mControllers.get(c).onUserRemovedLocked(userId);
896 }
897 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700898 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
899 // Has this package scheduled any jobs, such that we will take action
900 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700901 if (pkgUid != -1) {
902 List<JobStatus> jobsForUid;
903 synchronized (mLock) {
904 jobsForUid = mJobs.getJobsByUid(pkgUid);
905 }
906 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
907 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
908 if (DEBUG) {
909 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
910 + pkgUid + " has jobs");
911 }
912 setResultCode(Activity.RESULT_OK);
913 break;
914 }
915 }
916 }
917 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
918 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700919 if (pkgUid != -1) {
920 if (DEBUG) {
921 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
922 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700923 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700924 }
Christopher Tate7060b042014-06-09 19:50:00 -0700925 }
926 }
927 };
928
Christopher Tateb5c07882016-05-26 17:11:09 -0700929 private String getPackageName(Intent intent) {
930 Uri uri = intent.getData();
931 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
932 return pkg;
933 }
934
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700935 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700936 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800937 mHandler.obtainMessage(MSG_UID_STATE_CHANGED, uid, procState).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700938 }
939
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700940 @Override public void onUidGone(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800941 mHandler.obtainMessage(MSG_UID_GONE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700942 }
943
944 @Override public void onUidActive(int uid) throws RemoteException {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800945 mHandler.obtainMessage(MSG_UID_ACTIVE, uid, 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700946 }
947
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700948 @Override public void onUidIdle(int uid, boolean disabled) {
Makoto Onuki743e0ad2018-02-20 16:01:11 -0800949 mHandler.obtainMessage(MSG_UID_IDLE, uid, disabled ? 1 : 0).sendToTarget();
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700950 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700951
952 @Override public void onUidCachedChanged(int uid, boolean cached) {
953 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700954 };
955
Jeff Sharkey4d89e422018-03-29 18:22:41 -0600956 public Context getTestableContext() {
957 return getContext();
958 }
959
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800960 public Object getLock() {
961 return mLock;
962 }
963
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700964 public JobStore getJobStore() {
965 return mJobs;
966 }
967
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -0700968 public Constants getConstants() {
969 return mConstants;
970 }
971
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700972 @Override
973 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700974 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
975 // Let's kick any outstanding jobs for this user.
976 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
977 }
978
979 @Override
980 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700981 // Let's kick any outstanding jobs for this user.
982 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
983 }
984
985 @Override
986 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700987 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700988 }
989
Makoto Onuki15407842018-01-19 14:23:11 -0800990 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800991 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800992 */
Makoto Onukie4918212018-02-06 11:30:15 -0800993 private boolean isUidActive(int uid) {
994 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800995 }
996
Makoto Onukie4918212018-02-06 11:30:15 -0800997 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800998
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700999 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
1000 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001001 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -08001002 if (ActivityManager.getService().isAppStartModeDisabled(uId,
1003 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001004 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
1005 + " -- package not allowed to start");
1006 return JobScheduler.RESULT_FAILURE;
1007 }
1008 } catch (RemoteException e) {
1009 }
Christopher Tatea732f012017-10-26 17:26:53 -07001010
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001011 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001012 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
1013
1014 if (work != null && toCancel != null) {
1015 // Fast path: we are adding work to an existing job, and the JobInfo is not
1016 // changing. We can just directly enqueue this work in to the job.
1017 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -08001018
Dianne Hackborn342e6032017-04-13 18:04:31 -07001019 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -08001020
1021 // If any of work item is enqueued when the source is in the foreground,
1022 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -08001023 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001024
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001025 return JobScheduler.RESULT_SUCCESS;
1026 }
1027 }
1028
1029 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -08001030
1031 // Give exemption if the source is in the foreground just now.
1032 // Note if it's a sync job, this method is called on the handler so it's not exactly
1033 // the state when requestSync() was called, but that should be fine because of the
1034 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -08001035 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -08001036
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001037 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -08001038 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -08001039 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001040 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
1041 Slog.w(TAG, "Too many jobs for uid " + uId);
1042 throw new IllegalStateException("Apps may not schedule more than "
1043 + MAX_JOBS_PER_APP + " distinct jobs");
1044 }
1045 }
1046
Dianne Hackborna47223f2017-03-30 13:49:13 -07001047 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001048 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001049
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001050 if (work != null) {
1051 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001052 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001053 }
Christopher Tate16032042018-10-05 15:53:50 -07001054
1055 if (toCancel != null) {
1056 // Implicitly replaces the existing job record with the new instance
1057 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
1058 } else {
1059 startTrackingJobLocked(jobStatus, null);
1060 }
Tej Singhd5747a62018-01-08 20:57:35 -08001061 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -08001062 uId, null, jobStatus.getBatteryName(),
Tej Singh33a412b2018-03-16 18:43:59 -07001063 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED,
1064 JobProtoEnums.STOP_REASON_CANCELLED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001065
1066 // If the job is immediately ready to run, then we can just immediately
1067 // put it in the pending list and try to schedule it. This is especially
1068 // important for jobs with a 0 deadline constraint, since they will happen a fair
1069 // amount, we want to handle them as quickly as possible, and semantically we want to
1070 // make sure we have started holding the wake lock for the job before returning to
1071 // the caller.
1072 // If the job is not yet ready to run, there is nothing more to do -- we are
1073 // now just waiting for one of its controllers to change state and schedule
1074 // the job appropriately.
1075 if (isReadyToBeExecutedLocked(jobStatus)) {
1076 // This is a new job, we can just immediately put it on the pending
1077 // list and try to run it.
1078 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001079 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001080 maybeRunPendingJobsLocked();
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001081 } else {
1082 evaluateControllerStatesLocked(jobStatus);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001083 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001084 }
Christopher Tate7060b042014-06-09 19:50:00 -07001085 return JobScheduler.RESULT_SUCCESS;
1086 }
1087
1088 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001089 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08001090 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1091 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
1092 for (int i = jobs.size() - 1; i >= 0; i--) {
1093 JobStatus job = jobs.get(i);
1094 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -07001095 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001096 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -07001097 }
Christopher Tate7060b042014-06-09 19:50:00 -07001098 }
1099
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06001100 public JobInfo getPendingJob(int uid, int jobId) {
1101 synchronized (mLock) {
1102 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
1103 for (int i = jobs.size() - 1; i >= 0; i--) {
1104 JobStatus job = jobs.get(i);
1105 if (job.getJobId() == jobId) {
1106 return job.getJob();
1107 }
1108 }
1109 return null;
1110 }
1111 }
1112
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001113 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001114 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001115 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
1116 for (int i=0; i<jobsForUser.size(); i++) {
1117 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001118 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001119 }
Christopher Tate7060b042014-06-09 19:50:00 -07001120 }
1121 }
1122
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001123 private void cancelJobsForNonExistentUsers() {
1124 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
1125 synchronized (mLock) {
1126 mJobs.removeJobsOfNonUsers(umi.getUserIds());
1127 }
1128 }
1129
Makoto Onukie7b96182017-08-30 14:53:16 -07001130 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
1131 if ("android".equals(pkgName)) {
1132 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
1133 return;
1134 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001135 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001136 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1137 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
1138 final JobStatus job = jobsForUid.get(i);
1139 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001140 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001141 }
Christopher Tateee7805b2016-07-15 16:56:56 -07001142 }
1143 }
1144 }
1145
Christopher Tate7060b042014-06-09 19:50:00 -07001146 /**
1147 * Entry point from client to cancel all jobs originating from their uid.
1148 * This will remove the job from the master list, and cancel the job if it was staged for
1149 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -07001150 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -08001151 *
Christopher Tate7060b042014-06-09 19:50:00 -07001152 */
Christopher Tate8c67d122017-09-29 16:54:26 -07001153 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -07001154 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -07001155 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -07001156 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -07001157 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001158
1159 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001160 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001161 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
1162 for (int i=0; i<jobsForUid.size(); i++) {
1163 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -07001164 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -07001165 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001166 }
Christopher Tate7060b042014-06-09 19:50:00 -07001167 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001168 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -07001169 }
1170
1171 /**
1172 * Entry point from client to cancel the job corresponding to the jobId provided.
1173 * This will remove the job from the master list, and cancel the job if it was staged for
1174 * execution or being executed.
1175 * @param uid Uid of the calling client.
1176 * @param jobId Id of the job, provided at schedule-time.
1177 */
Makoto Onukid2bfec62018-01-12 13:58:01 -08001178 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -07001179 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001180 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -07001181 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001182 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -08001183 cancelJobImplLocked(toCancel, null,
1184 "cancel() called by app, callingUid=" + callingUid
1185 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001186 }
Christopher Tate8c67d122017-09-29 16:54:26 -07001187 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -07001188 }
1189 }
1190
Christopher Tate16032042018-10-05 15:53:50 -07001191 /**
1192 * Cancel the given job, stopping it if it's currently executing. If {@code incomingJob}
1193 * is null, the cancelled job is removed outright from the system. If
1194 * {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
1195 * currently scheduled jobs.
1196 */
Dianne Hackborn729a3282017-06-09 16:06:01 -07001197 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001198 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
1199 cancelled.unprepareLocked(ActivityManager.getService());
1200 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
1201 // Remove from pending queue.
1202 if (mPendingJobs.remove(cancelled)) {
1203 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001204 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001205 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001206 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Christopher Tate16032042018-10-05 15:53:50 -07001207 // If this is a replacement, bring in the new version of the job
1208 if (incomingJob != null) {
1209 if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
1210 startTrackingJobLocked(incomingJob, cancelled);
1211 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001212 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001213 }
1214
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001215 void updateUidState(int uid, int procState) {
1216 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001217 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1218 // Only use this if we are exactly the top app. All others can live
1219 // with just the foreground priority. This means that persistent processes
1220 // can never be the top app priority... that is fine.
1221 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001222 } else if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
1223 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_SERVICE);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001224 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Makoto Onukiec8b14d2018-12-05 13:22:24 -08001225 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001226 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001227 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001228 }
1229 }
1230 }
1231
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001232 @Override
1233 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001234 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001235 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001236 // When becoming idle, make sure no jobs are actively running,
1237 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001238 for (int i=0; i<mActiveServices.size(); i++) {
1239 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001240 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001241 if (executing != null
1242 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001243 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1244 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001245 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001246 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001247 } else {
1248 // When coming out of idle, allow thing to start back up.
1249 if (mReadyToRock) {
1250 if (mLocalDeviceIdleController != null) {
1251 if (!mReportedActive) {
1252 mReportedActive = true;
1253 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001254 }
1255 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001256 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001257 }
1258 }
1259 }
1260 }
1261
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001262 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001263 // active is true if pending queue contains jobs OR some job is running.
1264 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001265 if (mPendingJobs.size() <= 0) {
1266 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001267 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001268 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001269 if (job != null
1270 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
Christopher Tate20afddd2018-02-28 15:19:19 -08001271 && !job.dozeWhitelisted
1272 && !job.uidActive) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001273 // We will report active if we have a job running and it is not an exception
1274 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001275 active = true;
1276 break;
1277 }
1278 }
1279 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001280
1281 if (mReportedActive != active) {
1282 mReportedActive = active;
1283 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001284 mLocalDeviceIdleController.setJobsActive(active);
1285 }
1286 }
1287 }
1288
Christopher Tated117b292018-01-05 17:32:36 -08001289 void reportAppUsage(String packageName, int userId) {
1290 // This app just transitioned into interactive use or near equivalent, so we should
1291 // take a look at its job state for feedback purposes.
1292 }
1293
Christopher Tate7060b042014-06-09 19:50:00 -07001294 /**
1295 * Initializes the system service.
1296 * <p>
1297 * Subclasses must define a single argument constructor that accepts the context
1298 * and passes it to super.
1299 * </p>
1300 *
1301 * @param context The system server context.
1302 */
1303 public JobSchedulerService(Context context) {
1304 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001305
1306 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001307 mActivityManagerInternal = Preconditions.checkNotNull(
1308 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001309
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001310 mHandler = new JobHandler(context.getMainLooper());
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001311 mConstants = new Constants();
1312 mConstantsObserver = new ConstantsObserver(mHandler);
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001313 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001314
Makoto Onuki714f97d2018-12-05 11:18:13 -08001315 mConcurrencyManager = new JobConcurrencyManager(this);
1316
Christopher Tatea732f012017-10-26 17:26:53 -07001317 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001318 mStandbyTracker = new StandbyTracker();
1319 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1320 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001321
1322 // The job store needs to call back
1323 publishLocalService(JobSchedulerInternal.class, new LocalService());
1324
1325 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001326 mJobs = JobStore.initAndGet(this);
1327
Christopher Tate7060b042014-06-09 19:50:00 -07001328 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001329 mControllers = new ArrayList<StateController>();
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001330 mControllers.add(new ConnectivityController(this));
1331 mControllers.add(new TimeController(this));
1332 mControllers.add(new IdleController(this));
1333 mBatteryController = new BatteryController(this);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001334 mControllers.add(mBatteryController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001335 mStorageController = new StorageController(this);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001336 mControllers.add(mStorageController);
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001337 mControllers.add(new BackgroundJobsController(this));
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001338 mControllers.add(new ContentObserverController(this));
1339 mDeviceIdleJobsController = new DeviceIdleJobsController(this);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001340 mControllers.add(mDeviceIdleJobsController);
Kweku Adams4836f9d2018-11-12 17:04:17 -08001341 mControllers.add(new QuotaController(this));
Christopher Tate616541d2017-07-26 14:27:38 -07001342
1343 // If the job store determined that it can't yet reschedule persisted jobs,
1344 // we need to start watching the clock.
1345 if (!mJobs.jobTimesInflatedValid()) {
1346 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1347 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1348 }
Christopher Tate7060b042014-06-09 19:50:00 -07001349 }
1350
Christopher Tate616541d2017-07-26 14:27:38 -07001351 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1352 @Override
1353 public void onReceive(Context context, Intent intent) {
1354 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1355 // When we reach clock sanity, recalculate the temporal windows
1356 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001357 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001358 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1359
1360 // We've done our job now, so stop watching the time.
1361 context.unregisterReceiver(this);
1362
1363 // And kick off the work to update the affected jobs, using a secondary
1364 // thread instead of chugging away here on the main looper thread.
1365 FgThread.getHandler().post(mJobTimeUpdater);
1366 }
1367 }
1368 }
1369 };
1370
1371 private final Runnable mJobTimeUpdater = () -> {
1372 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1373 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1374 synchronized (mLock) {
1375 // Note: we intentionally both look up the existing affected jobs and replace them
1376 // with recalculated ones inside the same lock lifetime.
1377 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1378
1379 // Now, at each position [i], we have both the existing JobStatus
1380 // and the one that replaces it.
1381 final int N = toAdd.size();
1382 for (int i = 0; i < N; i++) {
1383 final JobStatus oldJob = toRemove.get(i);
1384 final JobStatus newJob = toAdd.get(i);
1385 if (DEBUG) {
1386 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1387 }
1388 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1389 }
1390 }
1391 };
1392
Christopher Tate7060b042014-06-09 19:50:00 -07001393 @Override
1394 public void onStart() {
1395 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1396 }
1397
1398 @Override
1399 public void onBootPhase(int phase) {
1400 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Jeff Sharkeyac2e8ef2018-02-22 16:06:44 -07001401 mConstantsObserver.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001402
Makoto Onukie4918212018-02-06 11:30:15 -08001403 mAppStateTracker = Preconditions.checkNotNull(
1404 LocalServices.getService(AppStateTracker.class));
Kweku Adams4836f9d2018-11-12 17:04:17 -08001405 if (mConstants.USE_HEARTBEATS) {
1406 setNextHeartbeatAlarm();
1407 }
Makoto Onuki15407842018-01-19 14:23:11 -08001408
Shreyas Basarge5db09082016-01-07 13:38:29 +00001409 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001410 final IntentFilter filter = new IntentFilter();
1411 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1412 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001413 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1414 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001415 filter.addDataScheme("package");
1416 getContext().registerReceiverAsUser(
1417 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1418 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1419 getContext().registerReceiverAsUser(
1420 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001421 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001422 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001423 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001424 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1425 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001426 } catch (RemoteException e) {
1427 // ignored; both services live in system_server
1428 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001429 // Remove any jobs that are not associated with any of the current users.
1430 cancelJobsForNonExistentUsers();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001431 // Register thermal callback
1432 mThermalService = IThermalService.Stub.asInterface(
1433 ServiceManager.getService(Context.THERMAL_SERVICE));
1434 if (mThermalService != null) {
1435 try {
1436 mThermalService.registerThermalStatusListener(new ThermalStatusListener());
1437 } catch (RemoteException e) {
1438 Slog.e(TAG, "Failed to register thermal callback.", e);
1439 }
1440 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001441 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001442 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001443 // Let's go!
1444 mReadyToRock = true;
1445 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1446 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001447 mLocalDeviceIdleController
1448 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001449 // Create the "runners".
1450 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1451 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001452 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001453 getContext().getMainLooper()));
1454 }
1455 // Attach jobs to their controllers.
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001456 mJobs.forEachJob((job) -> {
1457 for (int controller = 0; controller < mControllers.size(); controller++) {
1458 final StateController sc = mControllers.get(controller);
1459 sc.maybeStartTrackingJobLocked(job, null);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001460 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001461 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001462 // GO GO GO!
1463 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1464 }
Christopher Tate7060b042014-06-09 19:50:00 -07001465 }
1466 }
1467
1468 /**
1469 * Called when we have a job status object that we need to insert in our
1470 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1471 * about.
1472 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001473 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1474 if (!jobStatus.isPreparedLocked()) {
1475 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1476 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001477 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001478 final boolean update = mJobs.add(jobStatus);
1479 if (mReadyToRock) {
1480 for (int i = 0; i < mControllers.size(); i++) {
1481 StateController controller = mControllers.get(i);
1482 if (update) {
1483 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001484 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001485 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001486 }
Christopher Tate7060b042014-06-09 19:50:00 -07001487 }
1488 }
1489
1490 /**
1491 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1492 * object removed.
1493 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001494 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001495 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001496 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001497 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001498
1499 // Remove from store as well as controllers.
1500 final boolean removed = mJobs.remove(jobStatus, writeBack);
1501 if (removed && mReadyToRock) {
1502 for (int i=0; i<mControllers.size(); i++) {
1503 StateController controller = mControllers.get(i);
1504 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001505 }
1506 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001507 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001508 }
1509
Dianne Hackborn729a3282017-06-09 16:06:01 -07001510 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001511 for (int i=0; i<mActiveServices.size(); i++) {
1512 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001513 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001514 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001515 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001516 return true;
1517 }
1518 }
1519 return false;
1520 }
1521
1522 /**
1523 * @param job JobStatus we are querying against.
1524 * @return Whether or not the job represented by the status object is currently being run or
1525 * is pending.
1526 */
1527 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001528 for (int i=0; i<mActiveServices.size(); i++) {
1529 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001530 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001531 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1532 return true;
1533 }
1534 }
1535 return false;
1536 }
1537
Dianne Hackborn807de782016-04-07 17:54:41 -07001538 void noteJobsPending(List<JobStatus> jobs) {
1539 for (int i = jobs.size() - 1; i >= 0; i--) {
1540 JobStatus job = jobs.get(i);
1541 mJobPackageTracker.notePending(job);
1542 }
1543 }
1544
1545 void noteJobsNonpending(List<JobStatus> jobs) {
1546 for (int i = jobs.size() - 1; i >= 0; i--) {
1547 JobStatus job = jobs.get(i);
1548 mJobPackageTracker.noteNonpending(job);
1549 }
1550 }
1551
Christopher Tate7060b042014-06-09 19:50:00 -07001552 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001553 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1554 * specify an override deadline on a failed job (the failed job will run even though it's not
1555 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1556 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1557 *
Christopher Tate7060b042014-06-09 19:50:00 -07001558 * @param failureToReschedule Provided job status that we will reschedule.
1559 * @return A newly instantiated JobStatus with the same constraints as the last job except
1560 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001561 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001562 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001563 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001564 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001565 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001566 final JobInfo job = failureToReschedule.getJob();
1567
1568 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001569 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1570 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001571
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001572 if (failureToReschedule.hasWorkLocked()) {
1573 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1574 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1575 + backoffAttempts + " > work limit "
1576 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1577 return null;
1578 }
1579 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1580 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1581 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1582 return null;
1583 }
1584
Christopher Tate7060b042014-06-09 19:50:00 -07001585 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001586 case JobInfo.BACKOFF_POLICY_LINEAR: {
1587 long backoff = initialBackoffMillis;
1588 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1589 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1590 }
1591 delayMillis = backoff * backoffAttempts;
1592 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001593 default:
1594 if (DEBUG) {
1595 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1596 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001597 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1598 long backoff = initialBackoffMillis;
1599 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1600 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1601 }
1602 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1603 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001604 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001605 delayMillis =
1606 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001607 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1608 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001609 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001610 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001611 for (int ic=0; ic<mControllers.size(); ic++) {
1612 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001613 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001614 }
1615 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001616 }
1617
1618 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001619 * Called after a periodic has executed so we can reschedule it. We take the last execution
1620 * time of the job to be the time of completion (i.e. the time at which this function is
1621 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001622 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001623 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1624 * to underscheduling at least, rather than if we had taken the last execution time to be the
1625 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001626 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1627 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001628 * @return A new job representing the execution criteria for this instantiation of the
1629 * recurring job.
1630 */
1631 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001632 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001633 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001634 long runEarly = 0L;
1635
1636 // If this periodic was rescheduled it won't have a deadline.
1637 if (periodicToReschedule.hasDeadlineConstraint()) {
1638 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1639 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001640 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001641 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001642 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1643 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001644
1645 if (DEBUG) {
1646 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1647 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1648 }
Christopher Tatea732f012017-10-26 17:26:53 -07001649 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1650 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1651 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001652 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001653 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001654 }
1655
Christopher Tate325768c2018-03-07 16:07:56 -08001656 /*
1657 * We default to "long enough ago that every bucket's jobs are immediately runnable" to
1658 * avoid starvation of apps in uncommon-use buckets that might arise from repeated
1659 * reboot behavior.
1660 */
Christopher Tated1aebb32018-01-31 13:24:14 -08001661 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
Christopher Tate325768c2018-03-07 16:07:56 -08001662 // The furthest back in pre-boot time that we need to bother with
1663 long heartbeat = -mConstants.STANDBY_BEATS[RARE_INDEX];
1664 boolean cacheHit = false;
Christopher Tated1aebb32018-01-31 13:24:14 -08001665 synchronized (mLock) {
Christopher Tate325768c2018-03-07 16:07:56 -08001666 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1667 if (jobPackages != null) {
1668 long cachedValue = jobPackages.getOrDefault(packageName, Long.MAX_VALUE);
1669 if (cachedValue < Long.MAX_VALUE) {
1670 cacheHit = true;
1671 heartbeat = cachedValue;
1672 }
1673 }
1674 if (!cacheHit) {
1675 // We haven't seen it yet; ask usage stats about it
1676 final long timeSinceJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1677 if (timeSinceJob < Long.MAX_VALUE) {
1678 // Usage stats knows about it from before, so calculate back from that
1679 // and go from there.
1680 heartbeat = mHeartbeat - (timeSinceJob / mConstants.STANDBY_HEARTBEAT_TIME);
1681 }
1682 // If usage stats returned its "not found" MAX_VALUE, we still have the
1683 // negative default 'heartbeat' value we established above
1684 setLastJobHeartbeatLocked(packageName, userId, heartbeat);
1685 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001686 }
1687 if (DEBUG_STANDBY) {
Christopher Tate325768c2018-03-07 16:07:56 -08001688 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for "
1689 + packageName + "/" + userId);
Christopher Tated1aebb32018-01-31 13:24:14 -08001690 }
1691 return heartbeat;
1692 }
1693
1694 long heartbeatWhenJobsLastRun(JobStatus job) {
1695 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1696 }
1697
Christopher Tate325768c2018-03-07 16:07:56 -08001698 void setLastJobHeartbeatLocked(String packageName, int userId, long heartbeat) {
1699 HashMap<String, Long> jobPackages = mLastJobHeartbeats.get(userId);
1700 if (jobPackages == null) {
1701 jobPackages = new HashMap<>();
1702 mLastJobHeartbeats.put(userId, jobPackages);
1703 }
1704 jobPackages.put(packageName, heartbeat);
1705 }
1706
Christopher Tate7060b042014-06-09 19:50:00 -07001707 // JobCompletedListener implementations.
1708
1709 /**
1710 * A job just finished executing. We fetch the
1711 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
Christopher Tate325768c2018-03-07 16:07:56 -08001712 * whether we want to reschedule we re-add it to the controllers.
Christopher Tate7060b042014-06-09 19:50:00 -07001713 * @param jobStatus Completed job.
1714 * @param needsReschedule Whether the implementing class should reschedule this job.
1715 */
1716 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001717 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001718 if (DEBUG) {
1719 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1720 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001721
1722 // If the job wants to be rescheduled, we first need to make the next upcoming
1723 // job so we can transfer any appropriate state over from the previous job when
1724 // we stop it.
1725 final JobStatus rescheduledJob = needsReschedule
1726 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1727
Shreyas Basarge73f10252016-02-11 17:06:13 +00001728 // Do not write back immediately if this is a periodic job. The job may get lost if system
1729 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001730 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001731 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001732 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001733 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001734 // We still want to check for jobs to execute, because this job may have
1735 // scheduled a new job under the same job id, and now we can run it.
1736 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001737 return;
1738 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001739
1740 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001741 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001742 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001743 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001744 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001745 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001746 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001747 } else if (jobStatus.getJob().isPeriodic()) {
1748 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001749 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001750 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001751 } catch (SecurityException e) {
1752 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1753 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001754 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001755 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001756 jobStatus.unprepareLocked(ActivityManager.getService());
1757 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001758 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001759 }
1760
1761 // StateChangedListener implementations.
1762
1763 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001764 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1765 * some controller's state has changed, so as to run through the list of jobs and start/stop
1766 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001767 */
1768 @Override
1769 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001770 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001771 }
1772
1773 @Override
1774 public void onRunJobNow(JobStatus jobStatus) {
1775 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1776 }
1777
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001778 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001779
1780 public JobHandler(Looper looper) {
1781 super(looper);
1782 }
1783
1784 @Override
1785 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001786 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001787 if (!mReadyToRock) {
1788 return;
1789 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001790 switch (message.what) {
1791 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001792 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001793 // runNow can be null, which is a controller's way of indicating that its
1794 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001795 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001796 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001797 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001798 } else {
1799 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001800 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001801 } break;
1802 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001803 if (mReportedActive) {
1804 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001805 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001806 } else {
1807 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001808 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001809 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001810 break;
1811 case MSG_CHECK_JOB_GREEDY:
1812 queueReadyJobsForExecutionLocked();
1813 break;
1814 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001815 cancelJobImplLocked((JobStatus) message.obj, null,
1816 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001817 break;
Makoto Onuki743e0ad2018-02-20 16:01:11 -08001818
1819 case MSG_UID_STATE_CHANGED: {
1820 final int uid = message.arg1;
1821 final int procState = message.arg2;
1822 updateUidState(uid, procState);
1823 break;
1824 }
1825 case MSG_UID_GONE: {
1826 final int uid = message.arg1;
1827 final boolean disabled = message.arg2 != 0;
1828 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
1829 if (disabled) {
1830 cancelJobsForUid(uid, "uid gone");
1831 }
1832 synchronized (mLock) {
1833 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1834 }
1835 break;
1836 }
1837 case MSG_UID_ACTIVE: {
1838 final int uid = message.arg1;
1839 synchronized (mLock) {
1840 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
1841 }
1842 break;
1843 }
1844 case MSG_UID_IDLE: {
1845 final int uid = message.arg1;
1846 final boolean disabled = message.arg2 != 0;
1847 if (disabled) {
1848 cancelJobsForUid(uid, "app uid idle");
1849 }
1850 synchronized (mLock) {
1851 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
1852 }
1853 break;
1854 }
1855
Matthew Williams75fc5252014-09-02 16:17:53 -07001856 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001857 maybeRunPendingJobsLocked();
1858 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1859 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001860 }
1861 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001862 }
Christopher Tate7060b042014-06-09 19:50:00 -07001863
Wei Wang8c0c3c12018-11-14 14:56:52 -08001864 private boolean isJobThermalConstrainedLocked(JobStatus job) {
1865 return mThermalConstraint && job.hasConnectivityConstraint()
1866 && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
1867 }
1868
Dianne Hackborn6d068262017-05-16 13:14:37 -07001869 private void stopNonReadyActiveJobsLocked() {
1870 for (int i=0; i<mActiveServices.size(); i++) {
1871 JobServiceContext serviceContext = mActiveServices.get(i);
1872 final JobStatus running = serviceContext.getRunningJobLocked();
Wei Wang8c0c3c12018-11-14 14:56:52 -08001873 if (running == null) {
1874 continue;
1875 }
1876 if (!running.isReady()) {
Dianne Hackborn6d068262017-05-16 13:14:37 -07001877 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001878 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1879 "cancelled due to unsatisfied constraints");
Wei Wang8c0c3c12018-11-14 14:56:52 -08001880 } else if (isJobThermalConstrainedLocked(running)) {
1881 serviceContext.cancelExecutingJobLocked(
1882 JobParameters.REASON_DEVICE_THERMAL,
1883 "cancelled due to thermal condition");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001884 }
1885 }
1886 }
1887
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001888 /**
1889 * Run through list of jobs and execute all possible - at least one is expired so we do
1890 * as many as we can.
1891 */
1892 private void queueReadyJobsForExecutionLocked() {
1893 if (DEBUG) {
1894 Slog.d(TAG, "queuing all ready jobs for execution:");
1895 }
1896 noteJobsNonpending(mPendingJobs);
1897 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001898 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001899 mJobs.forEachJob(mReadyQueueFunctor);
1900 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001901
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001902 if (DEBUG) {
1903 final int queuedJobs = mPendingJobs.size();
1904 if (queuedJobs == 0) {
1905 Slog.d(TAG, "No jobs pending.");
1906 } else {
1907 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001908 }
1909 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001910 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001911
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001912 final class ReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001913 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001914
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001915 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001916 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001917 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001918 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001919 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001920 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001921 if (newReadyJobs == null) {
1922 newReadyJobs = new ArrayList<JobStatus>();
1923 }
1924 newReadyJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08001925 } else {
1926 evaluateControllerStatesLocked(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001927 }
1928 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001929
1930 public void postProcess() {
1931 if (newReadyJobs != null) {
1932 noteJobsPending(newReadyJobs);
1933 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001934 if (mPendingJobs.size() > 1) {
1935 mPendingJobs.sort(mEnqueueTimeComparator);
1936 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001937 }
1938 newReadyJobs = null;
1939 }
1940 }
1941 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1942
1943 /**
1944 * The state of at least one job has changed. Here is where we could enforce various
1945 * policies on when we want to execute jobs.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001946 */
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001947 final class MaybeReadyJobQueueFunctor implements Consumer<JobStatus> {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001948 int chargingCount;
1949 int batteryNotLowCount;
1950 int storageNotLowCount;
1951 int idleCount;
1952 int backoffCount;
1953 int connectivityCount;
1954 int contentCount;
1955 List<JobStatus> runnableJobs;
1956
1957 public MaybeReadyJobQueueFunctor() {
1958 reset();
1959 }
1960
1961 // Functor method invoked for each job via JobStore.forEachJob()
1962 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07001963 public void accept(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001964 if (isReadyToBeExecutedLocked(job)) {
1965 try {
1966 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1967 job.getJob().getService().getPackageName())) {
1968 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1969 + job.getJob().toString() + " -- package not allowed to start");
1970 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1971 return;
1972 }
1973 } catch (RemoteException e) {
1974 }
1975 if (job.getNumFailures() > 0) {
1976 backoffCount++;
1977 }
1978 if (job.hasIdleConstraint()) {
1979 idleCount++;
1980 }
1981 if (job.hasConnectivityConstraint()) {
1982 connectivityCount++;
1983 }
1984 if (job.hasChargingConstraint()) {
1985 chargingCount++;
1986 }
1987 if (job.hasBatteryNotLowConstraint()) {
1988 batteryNotLowCount++;
1989 }
1990 if (job.hasStorageNotLowConstraint()) {
1991 storageNotLowCount++;
1992 }
1993 if (job.hasContentTriggerConstraint()) {
1994 contentCount++;
1995 }
1996 if (runnableJobs == null) {
1997 runnableJobs = new ArrayList<>();
1998 }
1999 runnableJobs.add(job);
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002000 } else {
2001 evaluateControllerStatesLocked(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002002 }
2003 }
2004
2005 public void postProcess() {
2006 if (backoffCount > 0 ||
2007 idleCount >= mConstants.MIN_IDLE_COUNT ||
2008 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
2009 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
2010 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
2011 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
2012 contentCount >= mConstants.MIN_CONTENT_COUNT ||
2013 (runnableJobs != null
2014 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
2015 if (DEBUG) {
2016 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
2017 }
2018 noteJobsPending(runnableJobs);
2019 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002020 if (mPendingJobs.size() > 1) {
2021 mPendingJobs.sort(mEnqueueTimeComparator);
2022 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002023 } else {
2024 if (DEBUG) {
2025 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
2026 }
2027 }
2028
2029 // Be ready for next time
2030 reset();
2031 }
2032
2033 private void reset() {
2034 chargingCount = 0;
2035 idleCount = 0;
2036 backoffCount = 0;
2037 connectivityCount = 0;
2038 batteryNotLowCount = 0;
2039 storageNotLowCount = 0;
2040 contentCount = 0;
2041 runnableJobs = null;
2042 }
2043 }
2044 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
2045
2046 private void maybeQueueReadyJobsForExecutionLocked() {
2047 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
2048
2049 noteJobsNonpending(mPendingJobs);
2050 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07002051 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002052 mJobs.forEachJob(mMaybeQueueFunctor);
2053 mMaybeQueueFunctor.postProcess();
2054 }
2055
Christopher Tated1aebb32018-01-31 13:24:14 -08002056 /**
2057 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
2058 */
2059 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
2060
2061 @Override
2062 public void onAlarm() {
2063 synchronized (mLock) {
2064 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
2065 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
2066 if (beatsElapsed > 0) {
2067 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
2068 advanceHeartbeatLocked(beatsElapsed);
2069 }
Christopher Tatea732f012017-10-26 17:26:53 -07002070 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002071 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07002072 }
Christopher Tatea732f012017-10-26 17:26:53 -07002073 }
2074
Christopher Tated1aebb32018-01-31 13:24:14 -08002075 // Intentionally does not touch the alarm timing
2076 void advanceHeartbeatLocked(long beatsElapsed) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002077 if (!mConstants.USE_HEARTBEATS) {
2078 return;
2079 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002080 mHeartbeat += beatsElapsed;
2081 if (DEBUG_STANDBY) {
2082 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
2083 + " to " + mHeartbeat);
2084 }
2085 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
2086 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
2087 // new jobs scheduled by apps in that bucket will be permitted to run
2088 // immediately.
2089 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07002090 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002091 // Did we reach or cross a bucket boundary?
2092 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
2093 didAdvanceBucket = true;
2094 }
2095 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07002096 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
2097 }
2098 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08002099 Slog.v(TAG, " Bucket " + i + " next heartbeat "
2100 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07002101 }
2102 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002103
2104 if (didAdvanceBucket) {
2105 if (DEBUG_STANDBY) {
2106 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
2107 }
2108 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2109 }
2110 }
2111
2112 void setNextHeartbeatAlarm() {
2113 final long heartbeatLength;
2114 synchronized (mLock) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002115 if (!mConstants.USE_HEARTBEATS) {
2116 return;
2117 }
Christopher Tated1aebb32018-01-31 13:24:14 -08002118 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
2119 }
2120 final long now = sElapsedRealtimeClock.millis();
2121 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
2122 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
2123 if (DEBUG_STANDBY) {
2124 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
2125 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
2126 }
2127 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
2128 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
2129 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07002130 }
2131
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002132 /** Returns true if both the calling and source users for the job are started. */
2133 private boolean areUsersStartedLocked(final JobStatus job) {
2134 boolean sourceStarted = ArrayUtils.contains(mStartedUsers, job.getSourceUserId());
2135 if (job.getUserId() == job.getSourceUserId()) {
2136 return sourceStarted;
2137 }
2138 return sourceStarted && ArrayUtils.contains(mStartedUsers, job.getUserId());
2139 }
2140
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002141 /**
2142 * Criteria for moving a job into the pending queue:
2143 * - It's ready.
2144 * - It's not pending.
2145 * - It's not already running on a JSC.
2146 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07002147 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002148 * - The component is enabled and runnable.
2149 */
2150 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002151 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002152
2153 if (DEBUG) {
2154 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2155 + " ready=" + jobReady);
2156 }
2157
2158 // This is a condition that is very likely to be false (most jobs that are
2159 // scheduled are sitting there, not ready yet) and very cheap to check (just
2160 // a few conditions on data in JobStatus).
2161 if (!jobReady) {
Christopher Tate20afddd2018-02-28 15:19:19 -08002162 if (job.getSourcePackageName().equals("android.jobscheduler.cts.jobtestapp")) {
2163 Slog.v(TAG, " NOT READY: " + job);
2164 }
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002165 return false;
2166 }
2167
2168 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002169
2170 final int userId = job.getUserId();
2171 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
2172
2173 if (DEBUG) {
2174 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002175 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002176 }
2177
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002178 // These are also fairly cheap to check, though they typically will not
2179 // be conditions we fail.
2180 if (!jobExists || !userStarted) {
2181 return false;
2182 }
2183
Wei Wang8c0c3c12018-11-14 14:56:52 -08002184 if (isJobThermalConstrainedLocked(job)) {
2185 return false;
2186 }
2187
Dianne Hackborn28d1b662017-04-21 14:17:23 -07002188 final boolean jobPending = mPendingJobs.contains(job);
2189 final boolean jobActive = isCurrentlyActiveLocked(job);
2190
2191 if (DEBUG) {
2192 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2193 + " pending=" + jobPending + " active=" + jobActive);
2194 }
2195
2196 // These can be a little more expensive (especially jobActive, since we need to
2197 // go through the array of all potentially active jobs), so we are doing them
2198 // later... but still before checking with the package manager!
2199 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002200 return false;
2201 }
2202
Kweku Adams4836f9d2018-11-12 17:04:17 -08002203 if (mConstants.USE_HEARTBEATS) {
2204 // If the app is in a non-active standby bucket, make sure we've waited
2205 // an appropriate amount of time since the last invocation. During device-
2206 // wide parole, standby bucketing is ignored.
2207 //
2208 // Jobs in 'active' apps are not subject to standby, nor are jobs that are
2209 // specifically marked as exempt.
Christopher Tate20afddd2018-02-28 15:19:19 -08002210 if (DEBUG_STANDBY) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002211 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2212 + " parole=" + mInParole + " active=" + job.uidActive
2213 + " exempt=" + job.getJob().isExemptedFromAppStandby());
Christopher Tate20afddd2018-02-28 15:19:19 -08002214 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002215 if (!mInParole
2216 && !job.uidActive
2217 && !job.getJob().isExemptedFromAppStandby()) {
2218 final int bucket = job.getStandbyBucket();
2219 if (DEBUG_STANDBY) {
2220 Slog.v(TAG, " bucket=" + bucket + " heartbeat=" + mHeartbeat
2221 + " next=" + mNextBucketHeartbeat[bucket]);
2222 }
2223 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
2224 // Only skip this job if the app is still waiting for the end of its nominal
2225 // bucket interval. Once it's waited that long, we let it go ahead and clear.
2226 // The final (NEVER) bucket is special; we never age those apps' jobs into
2227 // runnability.
2228 final long appLastRan = heartbeatWhenJobsLastRun(job);
2229 if (bucket >= mConstants.STANDBY_BEATS.length
2230 || (mHeartbeat > appLastRan
2231 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
2232 // TODO: log/trace that we're deferring the job due to bucketing if we
2233 // hit this
2234 if (job.getWhenStandbyDeferred() == 0) {
2235 if (DEBUG_STANDBY) {
2236 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
2237 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
2238 + " for " + job);
2239 }
2240 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002241 }
Kweku Adams4836f9d2018-11-12 17:04:17 -08002242 return false;
2243 } else {
2244 if (DEBUG_STANDBY) {
2245 Slog.v(TAG, "Bucket deferred job aged into runnability at "
2246 + mHeartbeat + " : " + job);
2247 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002248 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08002249 }
Christopher Tatea732f012017-10-26 17:26:53 -07002250 }
Christopher Tatea732f012017-10-26 17:26:53 -07002251 }
2252
2253 // The expensive check last: validate that the defined package+service is
2254 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002255 final boolean componentPresent;
2256 try {
2257 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2258 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2259 userId) != null);
2260 } catch (RemoteException e) {
2261 throw e.rethrowAsRuntimeException();
2262 }
2263
2264 if (DEBUG) {
2265 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
2266 + " componentPresent=" + componentPresent);
2267 }
2268
2269 // Everything else checked out so far, so this is the final yes/no check
2270 return componentPresent;
2271 }
2272
Kweku Adamscdbfcb92018-12-06 17:05:15 -08002273 private void evaluateControllerStatesLocked(final JobStatus job) {
2274 for (int c = mControllers.size() - 1; c >= 0; --c) {
2275 final StateController sc = mControllers.get(c);
2276 sc.evaluateStateLocked(job);
2277 }
2278 }
2279
2280 /**
2281 * Returns true if non-job constraint components are in place -- if job.isReady() returns true
2282 * and this method returns true, then the job is ready to be executed.
2283 */
2284 public boolean areComponentsInPlaceLocked(JobStatus job) {
2285 // This code is very similar to the code in isReadyToBeExecutedLocked --- it uses the same
2286 // conditions.
2287
2288 final boolean jobExists = mJobs.containsJob(job);
2289 final boolean userStarted = areUsersStartedLocked(job);
2290
2291 if (DEBUG) {
2292 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2293 + " exists=" + jobExists + " userStarted=" + userStarted);
2294 }
2295
2296 // These are also fairly cheap to check, though they typically will not
2297 // be conditions we fail.
2298 if (!jobExists || !userStarted) {
2299 return false;
2300 }
2301
2302 // Job pending/active doesn't affect the readiness of a job.
2303
2304 // Skipping the hearbeat check as this will only come into play when using the rolling
2305 // window quota management system.
2306
2307 // The expensive check last: validate that the defined package+service is
2308 // still present & viable.
2309 final boolean componentPresent;
2310 try {
2311 // TODO: cache result until we're notified that something in the package changed.
2312 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2313 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2314 job.getUserId()) != null);
2315 } catch (RemoteException e) {
2316 throw e.rethrowAsRuntimeException();
2317 }
2318
2319 if (DEBUG) {
2320 Slog.v(TAG, "areComponentsInPlaceLocked: " + job.toShortString()
2321 + " componentPresent=" + componentPresent);
2322 }
2323
2324 // Everything else checked out so far, so this is the final yes/no check
2325 return componentPresent;
2326 }
2327
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002328 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002329 * Reconcile jobs in the pending queue against available execution contexts.
2330 * A controller can force a job into the pending queue even if it's already running, but
2331 * here is where we decide whether to actually execute it.
2332 */
2333 private void maybeRunPendingJobsLocked() {
2334 if (DEBUG) {
2335 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
2336 }
Makoto Onuki714f97d2018-12-05 11:18:13 -08002337 mConcurrencyManager.assignJobsToContextsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002338 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07002339 }
2340
Dianne Hackborn807de782016-04-07 17:54:41 -07002341 private int adjustJobPriority(int curPriority, JobStatus job) {
2342 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
2343 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002344 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002345 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002346 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002347 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
2348 }
2349 }
2350 return curPriority;
2351 }
2352
Makoto Onuki714f97d2018-12-05 11:18:13 -08002353 int evaluateJobPriorityLocked(JobStatus job) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002354 int priority = job.getPriority();
Makoto Onukiec8b14d2018-12-05 13:22:24 -08002355 if (priority >= JobInfo.PRIORITY_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002356 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002357 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002358 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
2359 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07002360 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002361 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002362 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002363 }
2364
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002365 final class LocalService implements JobSchedulerInternal {
2366
2367 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002368 * The current bucket heartbeat ordinal
2369 */
2370 public long currentHeartbeat() {
2371 return getCurrentHeartbeat();
2372 }
2373
2374 /**
2375 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2376 */
2377 public long nextHeartbeatForBucket(int bucket) {
2378 synchronized (mLock) {
2379 return mNextBucketHeartbeat[bucket];
2380 }
2381 }
2382
2383 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002384 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2385 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2386 * jobs in a long time is immediately runnable even if the app is bucketed into
2387 * an infrequent time allocation.
2388 */
2389 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2390 final int appStandbyBucket) {
2391 if (appStandbyBucket == 0 ||
2392 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2393 // ACTIVE => everything can be run right away
2394 // NEVER => we won't run them anyway, so let them go in the future
2395 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002396 if (DEBUG_STANDBY) {
2397 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2398 + packageName + "/" + userId);
2399 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002400 return 0;
2401 }
2402
Christopher Tated1aebb32018-01-31 13:24:14 -08002403 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2404 if (DEBUG_STANDBY) {
2405 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2406 + packageName + "/" + userId);
2407 }
2408 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002409 }
2410
Christopher Tate325768c2018-03-07 16:07:56 -08002411 public void noteJobStart(String packageName, int userId) {
2412 synchronized (mLock) {
2413 setLastJobHeartbeatLocked(packageName, userId, mHeartbeat);
2414 }
2415 }
2416
Christopher Tate435c2f42018-01-18 12:59:15 -08002417 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002418 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2419 * jobs are always considered pending.
2420 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002421 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002422 public List<JobInfo> getSystemScheduledPendingJobs() {
2423 synchronized (mLock) {
2424 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002425 mJobs.forEachJob(Process.SYSTEM_UID, (job) -> {
2426 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2427 pendingJobs.add(job.getJob());
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002428 }
2429 });
2430 return pendingJobs;
2431 }
2432 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002433
2434 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002435 public void cancelJobsForUid(int uid, String reason) {
2436 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2437 }
2438
2439 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002440 public void addBackingUpUid(int uid) {
2441 synchronized (mLock) {
2442 // No need to actually do anything here, since for a full backup the
2443 // activity manager will kill the process which will kill the job (and
2444 // cause it to restart, but now it can't run).
2445 mBackingUpUids.put(uid, uid);
2446 }
2447 }
2448
2449 @Override
2450 public void removeBackingUpUid(int uid) {
2451 synchronized (mLock) {
2452 mBackingUpUids.delete(uid);
2453 // If there are any jobs for this uid, we need to rebuild the pending list
2454 // in case they are now ready to run.
2455 if (mJobs.countJobsForUid(uid) > 0) {
2456 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2457 }
2458 }
2459 }
2460
2461 @Override
2462 public void clearAllBackingUpUids() {
2463 synchronized (mLock) {
2464 if (mBackingUpUids.size() > 0) {
2465 mBackingUpUids.clear();
2466 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2467 }
2468 }
2469 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002470
2471 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002472 public void reportAppUsage(String packageName, int userId) {
2473 JobSchedulerService.this.reportAppUsage(packageName, userId);
2474 }
2475
2476 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002477 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002478 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002479 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002480 }
2481 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002482 }
2483
Shreyas Basarge5db09082016-01-07 13:38:29 +00002484 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002485 * Tracking of app assignments to standby buckets
2486 */
2487 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002488
Christopher Tatea732f012017-10-26 17:26:53 -07002489 // AppIdleStateChangeListener interface for live updates
2490
2491 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002492 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Amith Yamasani119be9a2018-02-18 22:23:00 -08002493 boolean idle, int bucket, int reason) {
Kweku Adams4836f9d2018-11-12 17:04:17 -08002494 // QuotaController handles this now.
Christopher Tatea732f012017-10-26 17:26:53 -07002495 }
2496
2497 @Override
2498 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002499 if (DEBUG_STANDBY) {
2500 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2501 }
2502 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002503 }
Christopher Tated117b292018-01-05 17:32:36 -08002504
2505 @Override
2506 public void onUserInteractionStarted(String packageName, int userId) {
2507 final int uid = mLocalPM.getPackageUid(packageName,
2508 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2509 if (uid < 0) {
2510 // Quietly ignore; the case is already logged elsewhere
2511 return;
2512 }
2513
Amith Yamasani977e11f2018-02-16 11:29:54 -08002514 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2515 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2516 // Too long ago, not worth logging
2517 sinceLast = 0L;
2518 }
Christopher Tated117b292018-01-05 17:32:36 -08002519 final DeferredJobCounter counter = new DeferredJobCounter();
2520 synchronized (mLock) {
2521 mJobs.forEachJobForSourceUid(uid, counter);
2522 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002523 if (counter.numDeferred() > 0 || sinceLast > 0) {
2524 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2525 (BatteryStatsInternal.class);
2526 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
Yangster-mac96353002018-09-05 11:18:55 -07002527 StatsLog.write_non_chained(StatsLog.DEFERRED_JOB_STATS_REPORTED, uid, null,
2528 counter.numDeferred(), sinceLast);
Amith Yamasani977e11f2018-02-16 11:29:54 -08002529 }
Christopher Tated117b292018-01-05 17:32:36 -08002530 }
2531 }
2532
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002533 static class DeferredJobCounter implements Consumer<JobStatus> {
Christopher Tated117b292018-01-05 17:32:36 -08002534 private int mDeferred = 0;
2535
2536 public int numDeferred() {
2537 return mDeferred;
2538 }
2539
2540 @Override
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002541 public void accept(JobStatus job) {
Christopher Tated117b292018-01-05 17:32:36 -08002542 if (job.getWhenStandbyDeferred() > 0) {
2543 mDeferred++;
2544 }
2545 }
Christopher Tatea732f012017-10-26 17:26:53 -07002546 }
2547
2548 public static int standbyBucketToBucketIndex(int bucket) {
2549 // Normalize AppStandby constants to indices into our bookkeeping
Christopher Tatef2159712018-03-27 16:04:14 -07002550 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return NEVER_INDEX;
2551 else if (bucket > UsageStatsManager.STANDBY_BUCKET_FREQUENT) return RARE_INDEX;
2552 else if (bucket > UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return FREQUENT_INDEX;
2553 else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) return WORKING_INDEX;
2554 else return ACTIVE_INDEX;
Christopher Tatea732f012017-10-26 17:26:53 -07002555 }
2556
Christopher Tated1aebb32018-01-31 13:24:14 -08002557 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002558 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2559 UsageStatsManagerInternal usageStats = LocalServices.getService(
2560 UsageStatsManagerInternal.class);
2561 int bucket = usageStats != null
2562 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2563 : 0;
2564
2565 bucket = standbyBucketToBucketIndex(bucket);
2566
2567 if (DEBUG_STANDBY) {
2568 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2569 }
2570 return bucket;
2571 }
2572
2573 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002574 * Binder stub trampoline implementation
2575 */
2576 final class JobSchedulerStub extends IJobScheduler.Stub {
2577 /** Cache determination of whether a given app can persist jobs
2578 * key is uid of the calling app; value is undetermined/true/false
2579 */
2580 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2581
2582 // Enforce that only the app itself (or shared uid participant) can schedule a
2583 // job that runs one of the app's services, as well as verifying that the
2584 // named service properly requires the BIND_JOB_SERVICE permission
2585 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002586 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002587 final ComponentName service = job.getService();
2588 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002589 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002590 PackageManager.MATCH_DIRECT_BOOT_AWARE
2591 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002592 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002593 if (si == null) {
2594 throw new IllegalArgumentException("No such service " + service);
2595 }
Christopher Tate7060b042014-06-09 19:50:00 -07002596 if (si.applicationInfo.uid != uid) {
2597 throw new IllegalArgumentException("uid " + uid +
2598 " cannot schedule job in " + service.getPackageName());
2599 }
2600 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2601 throw new IllegalArgumentException("Scheduled service " + service
2602 + " does not require android.permission.BIND_JOB_SERVICE permission");
2603 }
Christopher Tate5568f542014-06-18 13:53:31 -07002604 } catch (RemoteException e) {
2605 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002606 }
2607 }
2608
2609 private boolean canPersistJobs(int pid, int uid) {
2610 // If we get this far we're good to go; all we need to do now is check
2611 // whether the app is allowed to persist its scheduled work.
2612 final boolean canPersist;
2613 synchronized (mPersistCache) {
2614 Boolean cached = mPersistCache.get(uid);
2615 if (cached != null) {
2616 canPersist = cached.booleanValue();
2617 } else {
2618 // Persisting jobs is tantamount to running at boot, so we permit
2619 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2620 // permission
2621 int result = getContext().checkPermission(
2622 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2623 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2624 mPersistCache.put(uid, canPersist);
2625 }
2626 }
2627 return canPersist;
2628 }
2629
Makoto Onuki959acb52018-01-26 14:10:03 -08002630 private void validateJobFlags(JobInfo job, int callingUid) {
2631 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2632 getContext().enforceCallingOrSelfPermission(
2633 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2634 }
2635 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2636 if (callingUid != Process.SYSTEM_UID) {
2637 throw new SecurityException("Job has invalid flags");
2638 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002639 if (job.isPeriodic()) {
2640 Slog.wtf(TAG, "Periodic jobs mustn't have"
2641 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002642 }
2643 }
2644 }
2645
Christopher Tate7060b042014-06-09 19:50:00 -07002646 // IJobScheduler implementation
2647 @Override
2648 public int schedule(JobInfo job) throws RemoteException {
2649 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002650 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002651 }
2652 final int pid = Binder.getCallingPid();
2653 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002654 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002655
2656 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002657 if (job.isPersisted()) {
2658 if (!canPersistJobs(pid, uid)) {
2659 throw new IllegalArgumentException("Error: requested job be persisted without"
2660 + " holding RECEIVE_BOOT_COMPLETED permission.");
2661 }
2662 }
Christopher Tate7060b042014-06-09 19:50:00 -07002663
Makoto Onuki959acb52018-01-26 14:10:03 -08002664 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002665
Christopher Tate7060b042014-06-09 19:50:00 -07002666 long ident = Binder.clearCallingIdentity();
2667 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002668 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2669 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002670 } finally {
2671 Binder.restoreCallingIdentity(ident);
2672 }
2673 }
2674
2675 // IJobScheduler implementation
2676 @Override
2677 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2678 if (DEBUG) {
2679 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2680 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002681 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002682 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002683
2684 enforceValidJobRequest(uid, job);
2685 if (job.isPersisted()) {
2686 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2687 }
2688 if (work == null) {
2689 throw new NullPointerException("work is null");
2690 }
2691
Makoto Onuki959acb52018-01-26 14:10:03 -08002692 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002693
2694 long ident = Binder.clearCallingIdentity();
2695 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002696 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2697 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002698 } finally {
2699 Binder.restoreCallingIdentity(ident);
2700 }
2701 }
2702
2703 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002704 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002705 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002706 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002707 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002708 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002709 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002710 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002711
2712 if (packageName == null) {
2713 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002714 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002715
2716 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2717 android.Manifest.permission.UPDATE_DEVICE_STATS);
2718 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2719 throw new SecurityException("Caller uid " + callerUid
2720 + " not permitted to schedule jobs for other apps");
2721 }
2722
Makoto Onuki959acb52018-01-26 14:10:03 -08002723 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002724
Shreyas Basarge968ac752016-01-11 23:09:26 +00002725 long ident = Binder.clearCallingIdentity();
2726 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002727 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002728 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002729 } finally {
2730 Binder.restoreCallingIdentity(ident);
2731 }
2732 }
2733
2734 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002735 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2736 final int uid = Binder.getCallingUid();
2737
2738 long ident = Binder.clearCallingIdentity();
2739 try {
2740 return JobSchedulerService.this.getPendingJobs(uid);
2741 } finally {
2742 Binder.restoreCallingIdentity(ident);
2743 }
2744 }
2745
2746 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002747 public JobInfo getPendingJob(int jobId) throws RemoteException {
2748 final int uid = Binder.getCallingUid();
2749
2750 long ident = Binder.clearCallingIdentity();
2751 try {
2752 return JobSchedulerService.this.getPendingJob(uid, jobId);
2753 } finally {
2754 Binder.restoreCallingIdentity(ident);
2755 }
2756 }
2757
2758 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002759 public void cancelAll() throws RemoteException {
2760 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002761 long ident = Binder.clearCallingIdentity();
2762 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002763 JobSchedulerService.this.cancelJobsForUid(uid,
2764 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002765 } finally {
2766 Binder.restoreCallingIdentity(ident);
2767 }
2768 }
2769
2770 @Override
2771 public void cancel(int jobId) throws RemoteException {
2772 final int uid = Binder.getCallingUid();
2773
2774 long ident = Binder.clearCallingIdentity();
2775 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002776 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002777 } finally {
2778 Binder.restoreCallingIdentity(ident);
2779 }
2780 }
2781
2782 /**
2783 * "dumpsys" infrastructure
2784 */
2785 @Override
2786 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002787 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002788
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002789 int filterUid = -1;
2790 boolean proto = false;
2791 if (!ArrayUtils.isEmpty(args)) {
2792 int opti = 0;
2793 while (opti < args.length) {
2794 String arg = args[opti];
2795 if ("-h".equals(arg)) {
2796 dumpHelp(pw);
2797 return;
2798 } else if ("-a".equals(arg)) {
2799 // Ignore, we always dump all.
2800 } else if ("--proto".equals(arg)) {
2801 proto = true;
2802 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2803 pw.println("Unknown option: " + arg);
2804 return;
2805 } else {
2806 break;
2807 }
2808 opti++;
2809 }
2810 if (opti < args.length) {
2811 String pkg = args[opti];
2812 try {
2813 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2814 PackageManager.MATCH_ANY_USER);
2815 } catch (NameNotFoundException ignored) {
2816 pw.println("Invalid package: " + pkg);
2817 return;
2818 }
2819 }
2820 }
2821
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002822 final long identityToken = Binder.clearCallingIdentity();
Christopher Tate7060b042014-06-09 19:50:00 -07002823 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002824 if (proto) {
2825 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2826 } else {
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07002827 JobSchedulerService.this.dumpInternal(new IndentingPrintWriter(pw, " "),
2828 filterUid);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002829 }
Christopher Tate7060b042014-06-09 19:50:00 -07002830 } finally {
2831 Binder.restoreCallingIdentity(identityToken);
2832 }
2833 }
Christopher Tate5d346052016-03-08 12:56:08 -08002834
2835 @Override
2836 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002837 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002838 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002839 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002840 }
Serik Beketayev75915d12018-08-01 16:56:59 -07002841
2842 /**
2843 * <b>For internal system user only!</b>
2844 * Returns a list of all currently-executing jobs.
2845 */
2846 @Override
2847 public List<JobInfo> getStartedJobs() {
2848 final int uid = Binder.getCallingUid();
2849 if (uid != Process.SYSTEM_UID) {
2850 throw new SecurityException(
2851 "getStartedJobs() is system internal use only.");
2852 }
2853
2854 final ArrayList<JobInfo> runningJobs;
2855
2856 synchronized (mLock) {
2857 runningJobs = new ArrayList<>(mActiveServices.size());
2858 for (JobServiceContext jsc : mActiveServices) {
2859 final JobStatus job = jsc.getRunningJobLocked();
2860 if (job != null) {
2861 runningJobs.add(job.getJob());
2862 }
2863 }
2864 }
2865
2866 return runningJobs;
2867 }
2868
2869 /**
2870 * <b>For internal system user only!</b>
2871 * Returns a snapshot of the state of all jobs known to the system.
2872 *
2873 * <p class="note">This is a slow operation, so it should be called sparingly.
2874 */
2875 @Override
2876 public List<JobSnapshot> getAllJobSnapshots() {
2877 final int uid = Binder.getCallingUid();
2878 if (uid != Process.SYSTEM_UID) {
2879 throw new SecurityException(
2880 "getAllJobSnapshots() is system internal use only.");
2881 }
2882 synchronized (mLock) {
2883 final ArrayList<JobSnapshot> snapshots = new ArrayList<>(mJobs.size());
2884 mJobs.forEachJob((job) -> snapshots.add(
2885 new JobSnapshot(job.getJob(), job.getSatisfiedConstraintFlags(),
2886 isReadyToBeExecutedLocked(job))));
2887 return snapshots;
2888 }
2889 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002890 };
2891
Christopher Tate5d346052016-03-08 12:56:08 -08002892 // Shell command infrastructure: run the given job immediately
2893 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2894 if (DEBUG) {
2895 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2896 + " " + jobId + " f=" + force);
2897 }
2898
2899 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002900 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2901 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002902 if (uid < 0) {
2903 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2904 }
2905
2906 synchronized (mLock) {
2907 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2908 if (js == null) {
2909 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2910 }
2911
2912 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2913 if (!js.isConstraintsSatisfied()) {
2914 js.overrideState = 0;
2915 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2916 }
2917
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002918 queueReadyJobsForExecutionLocked();
2919 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002920 }
2921 } catch (RemoteException e) {
2922 // can't happen
2923 }
2924 return 0;
2925 }
2926
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002927 // Shell command infrastructure: immediately timeout currently executing jobs
2928 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2929 boolean hasJobId, int jobId) {
2930 if (DEBUG) {
2931 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2932 }
2933
2934 synchronized (mLock) {
2935 boolean foundSome = false;
2936 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002937 final JobServiceContext jc = mActiveServices.get(i);
2938 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002939 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002940 foundSome = true;
2941 pw.print("Timing out: ");
2942 js.printUniqueId(pw);
2943 pw.print(" ");
2944 pw.println(js.getServiceComponent().flattenToShortString());
2945 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002946 }
2947 if (!foundSome) {
2948 pw.println("No matching executing jobs found.");
2949 }
2950 }
2951 return 0;
2952 }
2953
Christopher Tate8c67d122017-09-29 16:54:26 -07002954 // Shell command infrastructure: cancel a scheduled job
2955 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2956 boolean hasJobId, int jobId) {
2957 if (DEBUG) {
2958 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2959 }
2960
2961 int pkgUid = -1;
2962 try {
2963 IPackageManager pm = AppGlobals.getPackageManager();
2964 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2965 } catch (RemoteException e) { /* can't happen */ }
2966
2967 if (pkgUid < 0) {
2968 pw.println("Package " + pkgName + " not found.");
2969 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2970 }
2971
2972 if (!hasJobId) {
2973 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2974 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2975 pw.println("No matching jobs found.");
2976 }
2977 } else {
2978 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002979 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002980 pw.println("No matching job found.");
2981 }
2982 }
2983
2984 return 0;
2985 }
2986
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002987 void setMonitorBattery(boolean enabled) {
2988 synchronized (mLock) {
2989 if (mBatteryController != null) {
2990 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2991 }
2992 }
2993 }
2994
2995 int getBatterySeq() {
2996 synchronized (mLock) {
2997 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2998 }
2999 }
3000
3001 boolean getBatteryCharging() {
3002 synchronized (mLock) {
3003 return mBatteryController != null
3004 ? mBatteryController.getTracker().isOnStablePower() : false;
3005 }
3006 }
3007
3008 boolean getBatteryNotLow() {
3009 synchronized (mLock) {
3010 return mBatteryController != null
3011 ? mBatteryController.getTracker().isBatteryNotLow() : false;
3012 }
3013 }
3014
Dianne Hackborn532ea262017-03-17 17:50:55 -07003015 int getStorageSeq() {
3016 synchronized (mLock) {
3017 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
3018 }
3019 }
3020
3021 boolean getStorageNotLow() {
3022 synchronized (mLock) {
3023 return mStorageController != null
3024 ? mStorageController.getTracker().isStorageNotLow() : false;
3025 }
3026 }
3027
Christopher Tatea732f012017-10-26 17:26:53 -07003028 long getCurrentHeartbeat() {
3029 synchronized (mLock) {
3030 return mHeartbeat;
3031 }
3032 }
3033
Christopher Tated1aebb32018-01-31 13:24:14 -08003034 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07003035 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
3036 try {
3037 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
3038 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
3039 if (uid < 0) {
3040 pw.print("unknown("); pw.print(pkgName); pw.println(")");
3041 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
3042 }
3043
3044 synchronized (mLock) {
3045 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
3046 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
3047 if (js == null) {
3048 pw.print("unknown("); UserHandle.formatUid(pw, uid);
3049 pw.print("/jid"); pw.print(jobId); pw.println(")");
3050 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
3051 }
3052
3053 boolean printed = false;
3054 if (mPendingJobs.contains(js)) {
3055 pw.print("pending");
3056 printed = true;
3057 }
3058 if (isCurrentlyActiveLocked(js)) {
3059 if (printed) {
3060 pw.print(" ");
3061 }
3062 printed = true;
3063 pw.println("active");
3064 }
3065 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
3066 if (printed) {
3067 pw.print(" ");
3068 }
3069 printed = true;
3070 pw.println("user-stopped");
3071 }
3072 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
3073 if (printed) {
3074 pw.print(" ");
3075 }
3076 printed = true;
3077 pw.println("backing-up");
3078 }
3079 boolean componentPresent = false;
3080 try {
3081 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3082 js.getServiceComponent(),
3083 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3084 js.getUserId()) != null);
3085 } catch (RemoteException e) {
3086 }
3087 if (!componentPresent) {
3088 if (printed) {
3089 pw.print(" ");
3090 }
3091 printed = true;
3092 pw.println("no-component");
3093 }
3094 if (js.isReady()) {
3095 if (printed) {
3096 pw.print(" ");
3097 }
3098 printed = true;
3099 pw.println("ready");
3100 }
3101 if (!printed) {
3102 pw.print("waiting");
3103 }
3104 pw.println();
3105 }
3106 } catch (RemoteException e) {
3107 // can't happen
3108 }
3109 return 0;
3110 }
3111
Christopher Tated1aebb32018-01-31 13:24:14 -08003112 // Shell command infrastructure
3113 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
3114 if (numBeats < 1) {
3115 pw.println(getCurrentHeartbeat());
3116 return 0;
3117 }
3118
3119 pw.print("Advancing standby heartbeat by ");
3120 pw.println(numBeats);
3121 synchronized (mLock) {
3122 advanceHeartbeatLocked(numBeats);
3123 }
3124 return 0;
3125 }
3126
lpeter318abc92018-05-04 16:13:14 +08003127 void triggerDockState(boolean idleState) {
3128 final Intent dockIntent;
3129 if (idleState) {
3130 dockIntent = new Intent(Intent.ACTION_DOCK_IDLE);
3131 } else {
3132 dockIntent = new Intent(Intent.ACTION_DOCK_ACTIVE);
3133 }
3134 dockIntent.setPackage("android");
3135 dockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND);
3136 getContext().sendBroadcastAsUser(dockIntent, UserHandle.ALL);
3137 }
3138
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003139 static void dumpHelp(PrintWriter pw) {
3140 pw.println("Job Scheduler (jobscheduler) dump options:");
3141 pw.println(" [-h] [package] ...");
3142 pw.println(" -h: print this help");
3143 pw.println(" [package] is an optional package name to limit the output to.");
3144 }
3145
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003146 /** Sort jobs by caller UID, then by Job ID. */
3147 private static void sortJobs(List<JobStatus> jobs) {
3148 Collections.sort(jobs, new Comparator<JobStatus>() {
3149 @Override
3150 public int compare(JobStatus o1, JobStatus o2) {
3151 int uid1 = o1.getUid();
3152 int uid2 = o2.getUid();
3153 int id1 = o1.getJobId();
3154 int id2 = o2.getJobId();
3155 if (uid1 != uid2) {
3156 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003157 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003158 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003159 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003160 });
3161 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06003162
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003163 void dumpInternal(final IndentingPrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003164 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07003165 final long nowElapsed = sElapsedRealtimeClock.millis();
3166 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003167 final Predicate<JobStatus> predicate = (js) -> {
3168 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3169 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3170 };
Dianne Hackborn33d31c52016-02-16 10:30:33 -08003171 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003172 mConstants.dump(pw);
3173 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003174
3175 pw.println(" Heartbeat:");
3176 pw.print(" Current: "); pw.println(mHeartbeat);
3177 pw.println(" Next");
3178 pw.print(" ACTIVE: "); pw.println(mNextBucketHeartbeat[0]);
3179 pw.print(" WORKING: "); pw.println(mNextBucketHeartbeat[1]);
3180 pw.print(" FREQUENT: "); pw.println(mNextBucketHeartbeat[2]);
3181 pw.print(" RARE: "); pw.println(mNextBucketHeartbeat[3]);
3182 pw.print(" Last heartbeat: ");
3183 TimeUtils.formatDuration(mLastHeartbeatTime, nowElapsed, pw);
3184 pw.println();
3185 pw.print(" Next heartbeat: ");
3186 TimeUtils.formatDuration(mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME,
3187 nowElapsed, pw);
3188 pw.println();
Makoto Onuki0525b982018-05-02 14:46:59 -07003189 pw.print(" In parole?: ");
3190 pw.print(mInParole);
3191 pw.println();
Wei Wang8c0c3c12018-11-14 14:56:52 -08003192 pw.print(" In thermal throttling?: ");
3193 pw.print(mThermalConstraint);
3194 pw.println();
Makoto Onukib5d5e972018-02-20 14:44:20 -08003195 pw.println();
3196
Jeff Sharkey822cbd12016-02-25 11:09:55 -07003197 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003198 pw.print("Registered ");
3199 pw.print(mJobs.size());
3200 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07003201 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003202 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003203 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003204 for (JobStatus job : jobs) {
3205 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
3206 pw.println(job.toShortStringExceptUniqueId());
3207
3208 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003209 if (!predicate.test(job)) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003210 continue;
3211 }
3212
Dianne Hackborn6d068262017-05-16 13:14:37 -07003213 job.dump(pw, " ", true, nowElapsed);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003214 pw.print(" Last run heartbeat: ");
3215 pw.print(heartbeatWhenJobsLastRun(job));
3216 pw.println();
3217
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003218 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003219 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003220 pw.print(" (job=");
3221 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003222 pw.print(" user=");
3223 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003224 pw.print(" !pending=");
3225 pw.print(!mPendingJobs.contains(job));
3226 pw.print(" !active=");
3227 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003228 pw.print(" !backingup=");
3229 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07003230 pw.print(" comp=");
3231 boolean componentPresent = false;
3232 try {
3233 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3234 job.getServiceComponent(),
3235 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3236 job.getUserId()) != null);
3237 } catch (RemoteException e) {
3238 }
3239 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07003240 pw.println(")");
3241 }
Christopher Tate7060b042014-06-09 19:50:00 -07003242 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07003243 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07003244 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003245 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07003246 pw.println();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003247 pw.println(mControllers.get(i).getClass().getSimpleName() + ":");
3248 pw.increaseIndent();
3249 mControllers.get(i).dumpControllerStateLocked(pw, predicate);
3250 pw.decreaseIndent();
Christopher Tate7060b042014-06-09 19:50:00 -07003251 }
3252 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08003253 pw.println("Uid priority overrides:");
3254 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003255 int uid = mUidPriorityOverride.keyAt(i);
3256 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3257 pw.print(" "); pw.print(UserHandle.formatUid(uid));
3258 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
3259 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003260 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07003261 if (mBackingUpUids.size() > 0) {
3262 pw.println();
3263 pw.println("Backing up uids:");
3264 boolean first = true;
3265 for (int i = 0; i < mBackingUpUids.size(); i++) {
3266 int uid = mBackingUpUids.keyAt(i);
3267 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3268 if (first) {
3269 pw.print(" ");
3270 first = false;
3271 } else {
3272 pw.print(", ");
3273 }
3274 pw.print(UserHandle.formatUid(uid));
3275 }
3276 }
3277 pw.println();
3278 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003279 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003280 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07003281 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003282 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
3283 pw.println();
3284 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003285 pw.println("Pending queue:");
3286 for (int i=0; i<mPendingJobs.size(); i++) {
3287 JobStatus job = mPendingJobs.get(i);
3288 pw.print(" Pending #"); pw.print(i); pw.print(": ");
3289 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07003290 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003291 int priority = evaluateJobPriorityLocked(job);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003292 pw.print(" Evaluated priority: ");
3293 pw.println(JobInfo.getPriorityString(priority));
3294
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003295 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003296 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003297 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003298 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003299 }
Christopher Tate7060b042014-06-09 19:50:00 -07003300 pw.println();
3301 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003302 for (int i=0; i<mActiveServices.size(); i++) {
3303 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003304 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003305 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003306 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003307 if (jsc.mStoppedReason != null) {
3308 pw.print("inactive since ");
3309 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3310 pw.print(", stopped because: ");
3311 pw.println(jsc.mStoppedReason);
3312 } else {
3313 pw.println("inactive");
3314 }
Christopher Tate7060b042014-06-09 19:50:00 -07003315 continue;
3316 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003317 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003318 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003319 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003320 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003321 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003322 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003323 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003324 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003325 pw.print(" Evaluated priority: ");
3326 pw.println(JobInfo.getPriorityString(priority));
3327
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003328 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003329 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003330 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003331 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3332 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003333 }
3334 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003335 if (filterUid == -1) {
3336 pw.println();
3337 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3338 pw.print("mReportedActive="); pw.println(mReportedActive);
3339 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3340 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003341 pw.println();
3342 pw.print("PersistStats: ");
3343 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003344 }
3345 pw.println();
3346 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003347
3348 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3349 ProtoOutputStream proto = new ProtoOutputStream(fd);
3350 final int filterUidFinal = UserHandle.getAppId(filterUid);
3351 final long nowElapsed = sElapsedRealtimeClock.millis();
3352 final long nowUptime = sUptimeMillisClock.millis();
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003353 final Predicate<JobStatus> predicate = (js) -> {
3354 return filterUidFinal == -1 || UserHandle.getAppId(js.getUid()) == filterUidFinal
3355 || UserHandle.getAppId(js.getSourceUid()) == filterUidFinal;
3356 };
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003357
3358 synchronized (mLock) {
3359 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003360 proto.write(JobSchedulerServiceDumpProto.CURRENT_HEARTBEAT, mHeartbeat);
3361 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[0]);
3362 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[1]);
3363 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[2]);
3364 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT, mNextBucketHeartbeat[3]);
3365 proto.write(JobSchedulerServiceDumpProto.LAST_HEARTBEAT_TIME_MILLIS,
3366 mLastHeartbeatTime - nowUptime);
3367 proto.write(JobSchedulerServiceDumpProto.NEXT_HEARTBEAT_TIME_MILLIS,
3368 mLastHeartbeatTime + mConstants.STANDBY_HEARTBEAT_TIME - nowUptime);
Makoto Onuki0525b982018-05-02 14:46:59 -07003369 proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
Wei Wang8c0c3c12018-11-14 14:56:52 -08003370 proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003371
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003372 for (int u : mStartedUsers) {
3373 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3374 }
3375 if (mJobs.size() > 0) {
3376 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3377 sortJobs(jobs);
3378 for (JobStatus job : jobs) {
3379 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3380 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3381
3382 // Skip printing details if the caller requested a filter
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003383 if (!predicate.test(job)) {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003384 continue;
3385 }
3386
3387 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3388
3389 // isReadyToBeExecuted
3390 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3391 job.isReady());
3392 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3393 ArrayUtils.contains(mStartedUsers, job.getUserId()));
3394 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3395 mPendingJobs.contains(job));
3396 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3397 isCurrentlyActiveLocked(job));
3398 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3399 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3400 boolean componentPresent = false;
3401 try {
3402 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3403 job.getServiceComponent(),
3404 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3405 job.getUserId()) != null);
3406 } catch (RemoteException e) {
3407 }
3408 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3409 componentPresent);
Makoto Onukib5d5e972018-02-20 14:44:20 -08003410 proto.write(RegisteredJob.LAST_RUN_HEARTBEAT, heartbeatWhenJobsLastRun(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003411
3412 proto.end(rjToken);
3413 }
3414 }
3415 for (StateController controller : mControllers) {
3416 controller.dumpControllerStateLocked(
Jeff Sharkeyfee8c7b2018-02-21 22:18:45 -07003417 proto, JobSchedulerServiceDumpProto.CONTROLLERS, predicate);
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003418 }
3419 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3420 int uid = mUidPriorityOverride.keyAt(i);
3421 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3422 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3423 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3424 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3425 mUidPriorityOverride.valueAt(i));
3426 proto.end(pToken);
3427 }
3428 }
3429 for (int i = 0; i < mBackingUpUids.size(); i++) {
3430 int uid = mBackingUpUids.keyAt(i);
3431 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3432 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3433 }
3434 }
3435
3436 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3437 filterUidFinal);
3438 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3439 filterUidFinal);
3440
3441 for (JobStatus job : mPendingJobs) {
3442 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3443
3444 job.writeToShortProto(proto, PendingJob.INFO);
3445 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003446 proto.write(PendingJob.EVALUATED_PRIORITY, evaluateJobPriorityLocked(job));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003447 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3448
3449 proto.end(pjToken);
3450 }
3451 for (JobServiceContext jsc : mActiveServices) {
3452 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3453 final JobStatus job = jsc.getRunningJobLocked();
3454
3455 if (job == null) {
3456 final long ijToken = proto.start(ActiveJob.INACTIVE);
3457
3458 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3459 nowElapsed - jsc.mStoppedTime);
3460 if (jsc.mStoppedReason != null) {
3461 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3462 jsc.mStoppedReason);
3463 }
3464
3465 proto.end(ijToken);
3466 } else {
3467 final long rjToken = proto.start(ActiveJob.RUNNING);
3468
3469 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3470
3471 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3472 nowElapsed - jsc.getExecutionStartTimeElapsed());
3473 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3474 jsc.getTimeoutElapsed() - nowElapsed);
3475
3476 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3477
Makoto Onukiec8b14d2018-12-05 13:22:24 -08003478 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY,
3479 evaluateJobPriorityLocked(jsc.getRunningJobLocked()));
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003480
3481 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3482 nowUptime - job.madeActive);
3483 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3484 job.madeActive - job.madePending);
3485
3486 proto.end(rjToken);
3487 }
3488 proto.end(ajToken);
3489 }
3490 if (filterUid == -1) {
3491 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3492 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3493 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3494 }
3495 }
3496
3497 proto.flush();
3498 }
Christopher Tate7060b042014-06-09 19:50:00 -07003499}