blob: f3f4dfda82c8d66786eec5bf2cbfce3e278593fe [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;
Christopher Tate7060b042014-06-09 19:50:00 -070032import android.app.job.JobScheduler;
33import android.app.job.JobService;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070034import android.app.job.JobWorkItem;
Amith Yamasaniafbccb72017-11-27 10:44:24 -080035import android.app.usage.UsageStatsManager;
Christopher Tatea732f012017-10-26 17:26:53 -070036import android.app.usage.UsageStatsManagerInternal;
37import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener;
Christopher Tate7060b042014-06-09 19:50:00 -070038import android.content.BroadcastReceiver;
39import android.content.ComponentName;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070040import android.content.ContentResolver;
Christopher Tate7060b042014-06-09 19:50:00 -070041import android.content.Context;
42import android.content.Intent;
43import android.content.IntentFilter;
Christopher Tate5568f542014-06-18 13:53:31 -070044import android.content.pm.IPackageManager;
Christopher Tate7060b042014-06-09 19:50:00 -070045import android.content.pm.PackageManager;
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -060046import android.content.pm.PackageManager.NameNotFoundException;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070047import android.content.pm.PackageManagerInternal;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070048import android.content.pm.ServiceInfo;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070049import android.database.ContentObserver;
Christopher Tateb5c07882016-05-26 17:11:09 -070050import android.net.Uri;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070051import android.os.BatteryStats;
Amith Yamasani977e11f2018-02-16 11:29:54 -080052import android.os.BatteryStatsInternal;
Christopher Tate7060b042014-06-09 19:50:00 -070053import android.os.Binder;
54import android.os.Handler;
55import android.os.Looper;
56import android.os.Message;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070057import android.os.Process;
Christopher Tate7060b042014-06-09 19:50:00 -070058import android.os.RemoteException;
Christopher Tate5d346052016-03-08 12:56:08 -080059import android.os.ResultReceiver;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070060import android.os.ServiceManager;
Dianne Hackborn354736e2016-08-22 17:00:05 -070061import android.os.ShellCallback;
Christopher Tate7060b042014-06-09 19:50:00 -070062import android.os.SystemClock;
63import android.os.UserHandle;
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -070064import android.os.UserManagerInternal;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070065import android.provider.Settings;
Amith Yamasani977e11f2018-02-16 11:29:54 -080066import android.text.format.DateUtils;
Dianne Hackborne9a988c2016-05-27 17:59:40 -070067import android.util.KeyValueListParser;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070068import android.util.Log;
Christopher Tate7060b042014-06-09 19:50:00 -070069import android.util.Slog;
70import android.util.SparseArray;
Dianne Hackborn970510b2016-02-24 16:56:42 -080071import android.util.SparseIntArray;
Tej Singhd5747a62018-01-08 20:57:35 -080072import android.util.StatsLog;
Dianne Hackborn970510b2016-02-24 16:56:42 -080073import android.util.TimeUtils;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080074import android.util.proto.ProtoOutputStream;
Christopher Tate5d346052016-03-08 12:56:08 -080075
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070076import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070077import com.android.internal.app.IBatteryStats;
Joe Onorato4eb64fd2016-03-21 15:30:09 -070078import com.android.internal.app.procstats.ProcessStats;
Christopher Tatea732f012017-10-26 17:26:53 -070079import com.android.internal.os.BackgroundThread;
Jeff Sharkey822cbd12016-02-25 11:09:55 -070080import com.android.internal.util.ArrayUtils;
Jeff Sharkeyfe9a53b2017-03-31 14:08:23 -060081import com.android.internal.util.DumpUtils;
Makoto Onuki15407842018-01-19 14:23:11 -080082import com.android.internal.util.Preconditions;
Jeff Sharkey01bb5302018-02-21 20:12:40 -070083import com.android.server.AppStateTracker;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080084import com.android.server.DeviceIdleController;
Christopher Tate616541d2017-07-26 14:27:38 -070085import com.android.server.FgThread;
Dianne Hackborn627dfa12015-11-11 18:10:30 -080086import com.android.server.LocalServices;
Kweku Adams85f2fbc2017-12-18 12:04:12 -080087import com.android.server.job.JobSchedulerServiceDumpProto.ActiveJob;
88import com.android.server.job.JobSchedulerServiceDumpProto.PendingJob;
Christopher Tate2f36fd62016-02-18 18:36:08 -080089import com.android.server.job.JobStore.JobStatusFunctor;
Amith Yamasanib0ff3222015-03-04 09:56:14 -080090import com.android.server.job.controllers.AppIdleController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -070091import com.android.server.job.controllers.BackgroundJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070092import com.android.server.job.controllers.BatteryController;
93import com.android.server.job.controllers.ConnectivityController;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080094import com.android.server.job.controllers.ContentObserverController;
Amith Yamasanicb926fc2016-03-14 17:15:20 -070095import com.android.server.job.controllers.DeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -070096import com.android.server.job.controllers.IdleController;
97import com.android.server.job.controllers.JobStatus;
98import com.android.server.job.controllers.StateController;
Dianne Hackborn532ea262017-03-17 17:50:55 -070099import com.android.server.job.controllers.StorageController;
Christopher Tate7060b042014-06-09 19:50:00 -0700100import com.android.server.job.controllers.TimeController;
101
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700102import libcore.util.EmptyArray;
103
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700104import java.io.FileDescriptor;
105import java.io.PrintWriter;
106import java.time.Clock;
107import java.util.ArrayList;
108import java.util.Arrays;
109import java.util.Collections;
110import java.util.Comparator;
111import java.util.Iterator;
112import java.util.List;
Makoto Onuki15407842018-01-19 14:23:11 -0800113import java.util.function.Predicate;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700114
Christopher Tate7060b042014-06-09 19:50:00 -0700115/**
116 * Responsible for taking jobs representing work to be performed by a client app, and determining
117 * based on the criteria specified when that job should be run against the client application's
118 * endpoint.
119 * Implements logic for scheduling, and rescheduling jobs. The JobSchedulerService knows nothing
120 * about constraints, or the state of active jobs. It receives callbacks from the various
121 * controllers and completed jobs and operates accordingly.
122 *
123 * Note on locking: Any operations that manipulate {@link #mJobs} need to lock on that object.
124 * Any function with the suffix 'Locked' also needs to lock on {@link #mJobs}.
125 * @hide
126 */
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800127public final class JobSchedulerService extends com.android.server.SystemService
Matthew Williams01ac45b2014-07-22 20:44:12 -0700128 implements StateChangedListener, JobCompletedListener {
Jeff Sharkey01bb5302018-02-21 20:12:40 -0700129 public static final String TAG = "JobScheduler";
130 public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
Christopher Tatea732f012017-10-26 17:26:53 -0700131 public static final boolean DEBUG_STANDBY = DEBUG || false;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800132
Dianne Hackborn970510b2016-02-24 16:56:42 -0800133 /** The maximum number of concurrent jobs we run at one time. */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700134 private static final int MAX_JOB_CONTEXTS_COUNT = 16;
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800135 /** Enforce a per-app limit on scheduled jobs? */
Christopher Tate0213ace02016-02-24 14:18:35 -0800136 private static final boolean ENFORCE_MAX_JOBS = true;
Christopher Tate2f36fd62016-02-18 18:36:08 -0800137 /** The maximum number of jobs that we allow an unprivileged app to schedule */
138 private static final int MAX_JOBS_PER_APP = 100;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700139
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700140 @VisibleForTesting
141 public static Clock sSystemClock = Clock.systemUTC();
142 @VisibleForTesting
143 public static Clock sUptimeMillisClock = SystemClock.uptimeMillisClock();
144 @VisibleForTesting
145 public static Clock sElapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
Christopher Tate2f36fd62016-02-18 18:36:08 -0800146
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800147 /** Global local for all job scheduler state. */
148 final Object mLock = new Object();
Christopher Tate7060b042014-06-09 19:50:00 -0700149 /** Master list of jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700150 final JobStore mJobs;
Christopher Tatea732f012017-10-26 17:26:53 -0700151 /** Tracking the standby bucket state of each app */
152 final StandbyTracker mStandbyTracker;
Dianne Hackborn807de782016-04-07 17:54:41 -0700153 /** Tracking amount of time each package runs for. */
154 final JobPackageTracker mJobPackageTracker = new JobPackageTracker();
Christopher Tate7060b042014-06-09 19:50:00 -0700155
156 static final int MSG_JOB_EXPIRED = 0;
157 static final int MSG_CHECK_JOB = 1;
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700158 static final int MSG_STOP_JOB = 2;
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +0000159 static final int MSG_CHECK_JOB_GREEDY = 3;
Christopher Tate7060b042014-06-09 19:50:00 -0700160
Christopher Tate7060b042014-06-09 19:50:00 -0700161 /**
162 * Track Services that have currently active or pending jobs. The index is provided by
163 * {@link JobStatus#getServiceToken()}
164 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700165 final List<JobServiceContext> mActiveServices = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700166 /** List of controllers that will notify this service of updates to jobs. */
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700167 List<StateController> mControllers;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800168 /** Need direct access to this for testing. */
169 BatteryController mBatteryController;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700170 /** Need direct access to this for testing. */
171 StorageController mStorageController;
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -0700172 /** Need directly for sending uid state changes */
173 private BackgroundJobsController mBackgroundJobsController;
Suprabh Shukla106203b2017-11-02 21:23:44 -0700174 private DeviceIdleJobsController mDeviceIdleJobsController;
Christopher Tate7060b042014-06-09 19:50:00 -0700175 /**
176 * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
177 * when ready to execute them.
178 */
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700179 final ArrayList<JobStatus> mPendingJobs = new ArrayList<>();
Christopher Tate7060b042014-06-09 19:50:00 -0700180
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700181 int[] mStartedUsers = EmptyArray.INT;
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700182
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700183 final JobHandler mHandler;
184 final JobSchedulerStub mJobSchedulerStub;
185
Christopher Tatea732f012017-10-26 17:26:53 -0700186 PackageManagerInternal mLocalPM;
Makoto Onuki15407842018-01-19 14:23:11 -0800187 ActivityManagerInternal mActivityManagerInternal;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700188 IBatteryStats mBatteryStats;
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800189 DeviceIdleController.LocalService mLocalDeviceIdleController;
Makoto Onukie4918212018-02-06 11:30:15 -0800190 AppStateTracker mAppStateTracker;
Christopher Tated1aebb32018-01-31 13:24:14 -0800191 final UsageStatsManagerInternal mUsageStats;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700192
193 /**
194 * Set to true once we are allowed to run third party apps.
195 */
196 boolean mReadyToRock;
197
Christopher Tate7060b042014-06-09 19:50:00 -0700198 /**
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800199 * What we last reported to DeviceIdleController about whether we are active.
Dianne Hackborn627dfa12015-11-11 18:10:30 -0800200 */
201 boolean mReportedActive;
202
203 /**
Christopher Tatea5a85bd2018-01-03 17:20:36 -0800204 * Are we currently in device-wide standby parole?
205 */
206 volatile boolean mInParole;
207
208 /**
Dianne Hackborn970510b2016-02-24 16:56:42 -0800209 * Current limit on the number of concurrent JobServiceContext entries we want to
210 * keep actively running a job.
211 */
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700212 int mMaxActiveJobs = 1;
Dianne Hackborn970510b2016-02-24 16:56:42 -0800213
214 /**
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800215 * A mapping of which uids are currently in the foreground to their effective priority.
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800216 */
Dianne Hackborn970510b2016-02-24 16:56:42 -0800217 final SparseIntArray mUidPriorityOverride = new SparseIntArray();
218
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700219 /**
220 * Which uids are currently performing backups, so we shouldn't allow their jobs to run.
221 */
222 final SparseIntArray mBackingUpUids = new SparseIntArray();
223
Christopher Tatea732f012017-10-26 17:26:53 -0700224 /**
225 * Count standby heartbeats, and keep track of which beat each bucket's jobs will
226 * next become runnable. Index into this array is by normalized bucket:
227 * { ACTIVE, WORKING, FREQUENT, RARE, NEVER }. The ACTIVE and NEVER bucket
228 * milestones are not updated: ACTIVE apps get jobs whenever they ask for them,
229 * and NEVER apps don't get them at all.
230 */
231 final long[] mNextBucketHeartbeat = { 0, 0, 0, 0, Long.MAX_VALUE };
232 long mHeartbeat = 0;
Christopher Tated1aebb32018-01-31 13:24:14 -0800233 long mLastHeartbeatTime = sElapsedRealtimeClock.millis();
234
235 static final String HEARTBEAT_TAG = "*job.heartbeat*";
236 final HeartbeatAlarmListener mHeartbeatAlarm = new HeartbeatAlarmListener();
Christopher Tatea732f012017-10-26 17:26:53 -0700237
Dianne Hackborn970510b2016-02-24 16:56:42 -0800238 // -- Pre-allocated temporaries only for use in assignJobsToContextsLocked --
239
240 /**
241 * This array essentially stores the state of mActiveServices array.
242 * The ith index stores the job present on the ith JobServiceContext.
243 * We manipulate this array until we arrive at what jobs should be running on
244 * what JobServiceContext.
245 */
246 JobStatus[] mTmpAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
247 /**
248 * Indicates whether we need to act on this jobContext id
249 */
250 boolean[] mTmpAssignAct = new boolean[MAX_JOB_CONTEXTS_COUNT];
251 /**
252 * The uid whose jobs we would like to assign to a context.
253 */
254 int[] mTmpAssignPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800255
256 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700257 * All times are in milliseconds. These constants are kept synchronized with the system
258 * global Settings. Any access to this class or its fields should be done while
259 * holding the JobSchedulerService.mLock lock.
260 */
261 private final class Constants extends ContentObserver {
262 // Key names stored in the settings value.
263 private static final String KEY_MIN_IDLE_COUNT = "min_idle_count";
264 private static final String KEY_MIN_CHARGING_COUNT = "min_charging_count";
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800265 private static final String KEY_MIN_BATTERY_NOT_LOW_COUNT = "min_battery_not_low_count";
Dianne Hackborn532ea262017-03-17 17:50:55 -0700266 private static final String KEY_MIN_STORAGE_NOT_LOW_COUNT = "min_storage_not_low_count";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700267 private static final String KEY_MIN_CONNECTIVITY_COUNT = "min_connectivity_count";
268 private static final String KEY_MIN_CONTENT_COUNT = "min_content_count";
269 private static final String KEY_MIN_READY_JOBS_COUNT = "min_ready_jobs_count";
270 private static final String KEY_HEAVY_USE_FACTOR = "heavy_use_factor";
271 private static final String KEY_MODERATE_USE_FACTOR = "moderate_use_factor";
272 private static final String KEY_FG_JOB_COUNT = "fg_job_count";
273 private static final String KEY_BG_NORMAL_JOB_COUNT = "bg_normal_job_count";
274 private static final String KEY_BG_MODERATE_JOB_COUNT = "bg_moderate_job_count";
275 private static final String KEY_BG_LOW_JOB_COUNT = "bg_low_job_count";
276 private static final String KEY_BG_CRITICAL_JOB_COUNT = "bg_critical_job_count";
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700277 private static final String KEY_MAX_STANDARD_RESCHEDULE_COUNT
278 = "max_standard_reschedule_count";
279 private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
280 private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
281 private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
Christopher Tatea732f012017-10-26 17:26:53 -0700282 private static final String KEY_STANDBY_HEARTBEAT_TIME = "standby_heartbeat_time";
283 private static final String KEY_STANDBY_WORKING_BEATS = "standby_working_beats";
284 private static final String KEY_STANDBY_FREQUENT_BEATS = "standby_frequent_beats";
285 private static final String KEY_STANDBY_RARE_BEATS = "standby_rare_beats";
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700286
287 private static final int DEFAULT_MIN_IDLE_COUNT = 1;
288 private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800289 private static final int DEFAULT_MIN_BATTERY_NOT_LOW_COUNT = 1;
Dianne Hackborn532ea262017-03-17 17:50:55 -0700290 private static final int DEFAULT_MIN_STORAGE_NOT_LOW_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700291 private static final int DEFAULT_MIN_CONNECTIVITY_COUNT = 1;
292 private static final int DEFAULT_MIN_CONTENT_COUNT = 1;
293 private static final int DEFAULT_MIN_READY_JOBS_COUNT = 1;
294 private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
295 private static final float DEFAULT_MODERATE_USE_FACTOR = .5f;
296 private static final int DEFAULT_FG_JOB_COUNT = 4;
297 private static final int DEFAULT_BG_NORMAL_JOB_COUNT = 6;
298 private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
Nancy Zhenge39a8a42016-10-05 16:27:14 -0700299 private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700300 private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700301 private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
302 private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
303 private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
304 private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
Christopher Tatea732f012017-10-26 17:26:53 -0700305 private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
Esteban Talavera65254042017-12-15 10:59:28 +0000306 private static final int DEFAULT_STANDBY_WORKING_BEATS = 11; // ~ 2 hours, with 11min beats
307 private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
Christopher Tatea732f012017-10-26 17:26:53 -0700308 private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700309
310 /**
311 * Minimum # of idle jobs that must be ready in order to force the JMS to schedule things
312 * early.
313 */
314 int MIN_IDLE_COUNT = DEFAULT_MIN_IDLE_COUNT;
315 /**
316 * Minimum # of charging jobs that must be ready in order to force the JMS to schedule
317 * things early.
318 */
319 int MIN_CHARGING_COUNT = DEFAULT_MIN_CHARGING_COUNT;
320 /**
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800321 * Minimum # of "battery not low" jobs that must be ready in order to force the JMS to
322 * schedule things early.
323 */
324 int MIN_BATTERY_NOT_LOW_COUNT = DEFAULT_MIN_BATTERY_NOT_LOW_COUNT;
325 /**
Dianne Hackborn532ea262017-03-17 17:50:55 -0700326 * Minimum # of "storage not low" jobs that must be ready in order to force the JMS to
327 * schedule things early.
328 */
329 int MIN_STORAGE_NOT_LOW_COUNT = DEFAULT_MIN_STORAGE_NOT_LOW_COUNT;
330 /**
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700331 * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule
332 * things early. 1 == Run connectivity jobs as soon as ready.
333 */
334 int MIN_CONNECTIVITY_COUNT = DEFAULT_MIN_CONNECTIVITY_COUNT;
335 /**
336 * Minimum # of content trigger jobs that must be ready in order to force the JMS to
337 * schedule things early.
338 */
339 int MIN_CONTENT_COUNT = DEFAULT_MIN_CONTENT_COUNT;
340 /**
341 * Minimum # of jobs (with no particular constraints) for which the JMS will be happy
342 * running some work early. This (and thus the other min counts) is now set to 1, to
343 * prevent any batching at this level. Since we now do batching through doze, that is
344 * a much better mechanism.
345 */
346 int MIN_READY_JOBS_COUNT = DEFAULT_MIN_READY_JOBS_COUNT;
347 /**
348 * This is the job execution factor that is considered to be heavy use of the system.
349 */
350 float HEAVY_USE_FACTOR = DEFAULT_HEAVY_USE_FACTOR;
351 /**
352 * This is the job execution factor that is considered to be moderate use of the system.
353 */
354 float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
355 /**
356 * The number of MAX_JOB_CONTEXTS_COUNT we reserve for the foreground app.
357 */
358 int FG_JOB_COUNT = DEFAULT_FG_JOB_COUNT;
359 /**
360 * The maximum number of background jobs we allow when the system is in a normal
361 * memory state.
362 */
363 int BG_NORMAL_JOB_COUNT = DEFAULT_BG_NORMAL_JOB_COUNT;
364 /**
365 * The maximum number of background jobs we allow when the system is in a moderate
366 * memory state.
367 */
368 int BG_MODERATE_JOB_COUNT = DEFAULT_BG_MODERATE_JOB_COUNT;
369 /**
370 * The maximum number of background jobs we allow when the system is in a low
371 * memory state.
372 */
373 int BG_LOW_JOB_COUNT = DEFAULT_BG_LOW_JOB_COUNT;
374 /**
375 * The maximum number of background jobs we allow when the system is in a critical
376 * memory state.
377 */
378 int BG_CRITICAL_JOB_COUNT = DEFAULT_BG_CRITICAL_JOB_COUNT;
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700379 /**
380 * The maximum number of times we allow a job to have itself rescheduled before
381 * giving up on it, for standard jobs.
382 */
383 int MAX_STANDARD_RESCHEDULE_COUNT = DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT;
384 /**
385 * The maximum number of times we allow a job to have itself rescheduled before
386 * giving up on it, for jobs that are executing work.
387 */
388 int MAX_WORK_RESCHEDULE_COUNT = DEFAULT_MAX_WORK_RESCHEDULE_COUNT;
389 /**
390 * The minimum backoff time to allow for linear backoff.
391 */
392 long MIN_LINEAR_BACKOFF_TIME = DEFAULT_MIN_LINEAR_BACKOFF_TIME;
393 /**
394 * The minimum backoff time to allow for exponential backoff.
395 */
396 long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
Christopher Tatea732f012017-10-26 17:26:53 -0700397 /**
398 * How often we recalculate runnability based on apps' standby bucket assignment.
399 * This should be prime relative to common time interval lengths such as a quarter-
400 * hour or day, so that the heartbeat drifts relative to wall-clock milestones.
401 */
402 long STANDBY_HEARTBEAT_TIME = DEFAULT_STANDBY_HEARTBEAT_TIME;
403
404 /**
405 * Mapping: standby bucket -> number of heartbeats between each sweep of that
406 * bucket's jobs.
407 *
408 * Bucket assignments as recorded in the JobStatus objects are normalized to be
409 * indices into this array, rather than the raw constants used
410 * by AppIdleHistory.
411 */
412 final int[] STANDBY_BEATS = {
413 0,
414 DEFAULT_STANDBY_WORKING_BEATS,
415 DEFAULT_STANDBY_FREQUENT_BEATS,
416 DEFAULT_STANDBY_RARE_BEATS
417 };
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700418
419 private ContentResolver mResolver;
420 private final KeyValueListParser mParser = new KeyValueListParser(',');
421
422 public Constants(Handler handler) {
423 super(handler);
424 }
425
426 public void start(ContentResolver resolver) {
427 mResolver = resolver;
428 mResolver.registerContentObserver(Settings.Global.getUriFor(
429 Settings.Global.JOB_SCHEDULER_CONSTANTS), false, this);
430 updateConstants();
431 }
432
433 @Override
434 public void onChange(boolean selfChange, Uri uri) {
435 updateConstants();
436 }
437
438 private void updateConstants() {
439 synchronized (mLock) {
440 try {
441 mParser.setString(Settings.Global.getString(mResolver,
Suprabh Shukla7a0a5c42017-07-25 21:07:13 +0000442 Settings.Global.JOB_SCHEDULER_CONSTANTS));
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700443 } catch (IllegalArgumentException e) {
444 // Failed to parse the settings string, log this and move on
445 // with defaults.
Suprabh Shukla7a0a5c42017-07-25 21:07:13 +0000446 Slog.e(TAG, "Bad jobscheduler settings", e);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700447 }
448
449 MIN_IDLE_COUNT = mParser.getInt(KEY_MIN_IDLE_COUNT,
450 DEFAULT_MIN_IDLE_COUNT);
451 MIN_CHARGING_COUNT = mParser.getInt(KEY_MIN_CHARGING_COUNT,
452 DEFAULT_MIN_CHARGING_COUNT);
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800453 MIN_BATTERY_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_BATTERY_NOT_LOW_COUNT,
454 DEFAULT_MIN_BATTERY_NOT_LOW_COUNT);
Dianne Hackborn532ea262017-03-17 17:50:55 -0700455 MIN_STORAGE_NOT_LOW_COUNT = mParser.getInt(KEY_MIN_STORAGE_NOT_LOW_COUNT,
456 DEFAULT_MIN_STORAGE_NOT_LOW_COUNT);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700457 MIN_CONNECTIVITY_COUNT = mParser.getInt(KEY_MIN_CONNECTIVITY_COUNT,
458 DEFAULT_MIN_CONNECTIVITY_COUNT);
459 MIN_CONTENT_COUNT = mParser.getInt(KEY_MIN_CONTENT_COUNT,
460 DEFAULT_MIN_CONTENT_COUNT);
461 MIN_READY_JOBS_COUNT = mParser.getInt(KEY_MIN_READY_JOBS_COUNT,
462 DEFAULT_MIN_READY_JOBS_COUNT);
463 HEAVY_USE_FACTOR = mParser.getFloat(KEY_HEAVY_USE_FACTOR,
464 DEFAULT_HEAVY_USE_FACTOR);
465 MODERATE_USE_FACTOR = mParser.getFloat(KEY_MODERATE_USE_FACTOR,
466 DEFAULT_MODERATE_USE_FACTOR);
467 FG_JOB_COUNT = mParser.getInt(KEY_FG_JOB_COUNT,
468 DEFAULT_FG_JOB_COUNT);
469 BG_NORMAL_JOB_COUNT = mParser.getInt(KEY_BG_NORMAL_JOB_COUNT,
470 DEFAULT_BG_NORMAL_JOB_COUNT);
471 if ((FG_JOB_COUNT+BG_NORMAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
472 BG_NORMAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
473 }
474 BG_MODERATE_JOB_COUNT = mParser.getInt(KEY_BG_MODERATE_JOB_COUNT,
475 DEFAULT_BG_MODERATE_JOB_COUNT);
476 if ((FG_JOB_COUNT+BG_MODERATE_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
477 BG_MODERATE_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
478 }
479 BG_LOW_JOB_COUNT = mParser.getInt(KEY_BG_LOW_JOB_COUNT,
480 DEFAULT_BG_LOW_JOB_COUNT);
481 if ((FG_JOB_COUNT+BG_LOW_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
482 BG_LOW_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
483 }
484 BG_CRITICAL_JOB_COUNT = mParser.getInt(KEY_BG_CRITICAL_JOB_COUNT,
485 DEFAULT_BG_CRITICAL_JOB_COUNT);
486 if ((FG_JOB_COUNT+BG_CRITICAL_JOB_COUNT) > MAX_JOB_CONTEXTS_COUNT) {
487 BG_CRITICAL_JOB_COUNT = MAX_JOB_CONTEXTS_COUNT - FG_JOB_COUNT;
488 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700489 MAX_STANDARD_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_STANDARD_RESCHEDULE_COUNT,
490 DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT);
491 MAX_WORK_RESCHEDULE_COUNT = mParser.getInt(KEY_MAX_WORK_RESCHEDULE_COUNT,
492 DEFAULT_MAX_WORK_RESCHEDULE_COUNT);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800493 MIN_LINEAR_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_LINEAR_BACKOFF_TIME,
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700494 DEFAULT_MIN_LINEAR_BACKOFF_TIME);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800495 MIN_EXP_BACKOFF_TIME = mParser.getDurationMillis(KEY_MIN_EXP_BACKOFF_TIME,
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700496 DEFAULT_MIN_EXP_BACKOFF_TIME);
Amith Yamasani761d3ff2017-12-14 17:50:03 -0800497 STANDBY_HEARTBEAT_TIME = mParser.getDurationMillis(KEY_STANDBY_HEARTBEAT_TIME,
Christopher Tatea732f012017-10-26 17:26:53 -0700498 DEFAULT_STANDBY_HEARTBEAT_TIME);
499 STANDBY_BEATS[1] = mParser.getInt(KEY_STANDBY_WORKING_BEATS,
500 DEFAULT_STANDBY_WORKING_BEATS);
501 STANDBY_BEATS[2] = mParser.getInt(KEY_STANDBY_FREQUENT_BEATS,
502 DEFAULT_STANDBY_FREQUENT_BEATS);
503 STANDBY_BEATS[3] = mParser.getInt(KEY_STANDBY_RARE_BEATS,
504 DEFAULT_STANDBY_RARE_BEATS);
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700505 }
Christopher Tated1aebb32018-01-31 13:24:14 -0800506
507 // Reset the heartbeat alarm based on the new heartbeat duration
508 setNextHeartbeatAlarm();
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700509 }
510
511 void dump(PrintWriter pw) {
512 pw.println(" Settings:");
513
514 pw.print(" "); pw.print(KEY_MIN_IDLE_COUNT); pw.print("=");
515 pw.print(MIN_IDLE_COUNT); pw.println();
516
517 pw.print(" "); pw.print(KEY_MIN_CHARGING_COUNT); pw.print("=");
518 pw.print(MIN_CHARGING_COUNT); pw.println();
519
Dianne Hackborna06ec6a2017-02-13 10:08:42 -0800520 pw.print(" "); pw.print(KEY_MIN_BATTERY_NOT_LOW_COUNT); pw.print("=");
521 pw.print(MIN_BATTERY_NOT_LOW_COUNT); pw.println();
522
Dianne Hackborn532ea262017-03-17 17:50:55 -0700523 pw.print(" "); pw.print(KEY_MIN_STORAGE_NOT_LOW_COUNT); pw.print("=");
524 pw.print(MIN_STORAGE_NOT_LOW_COUNT); pw.println();
525
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700526 pw.print(" "); pw.print(KEY_MIN_CONNECTIVITY_COUNT); pw.print("=");
527 pw.print(MIN_CONNECTIVITY_COUNT); pw.println();
528
529 pw.print(" "); pw.print(KEY_MIN_CONTENT_COUNT); pw.print("=");
530 pw.print(MIN_CONTENT_COUNT); pw.println();
531
532 pw.print(" "); pw.print(KEY_MIN_READY_JOBS_COUNT); pw.print("=");
533 pw.print(MIN_READY_JOBS_COUNT); pw.println();
534
535 pw.print(" "); pw.print(KEY_HEAVY_USE_FACTOR); pw.print("=");
536 pw.print(HEAVY_USE_FACTOR); pw.println();
537
538 pw.print(" "); pw.print(KEY_MODERATE_USE_FACTOR); pw.print("=");
539 pw.print(MODERATE_USE_FACTOR); pw.println();
540
541 pw.print(" "); pw.print(KEY_FG_JOB_COUNT); pw.print("=");
542 pw.print(FG_JOB_COUNT); pw.println();
543
544 pw.print(" "); pw.print(KEY_BG_NORMAL_JOB_COUNT); pw.print("=");
545 pw.print(BG_NORMAL_JOB_COUNT); pw.println();
546
547 pw.print(" "); pw.print(KEY_BG_MODERATE_JOB_COUNT); pw.print("=");
548 pw.print(BG_MODERATE_JOB_COUNT); pw.println();
549
550 pw.print(" "); pw.print(KEY_BG_LOW_JOB_COUNT); pw.print("=");
551 pw.print(BG_LOW_JOB_COUNT); pw.println();
552
553 pw.print(" "); pw.print(KEY_BG_CRITICAL_JOB_COUNT); pw.print("=");
554 pw.print(BG_CRITICAL_JOB_COUNT); pw.println();
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700555
556 pw.print(" "); pw.print(KEY_MAX_STANDARD_RESCHEDULE_COUNT); pw.print("=");
557 pw.print(MAX_STANDARD_RESCHEDULE_COUNT); pw.println();
558
559 pw.print(" "); pw.print(KEY_MAX_WORK_RESCHEDULE_COUNT); pw.print("=");
560 pw.print(MAX_WORK_RESCHEDULE_COUNT); pw.println();
561
562 pw.print(" "); pw.print(KEY_MIN_LINEAR_BACKOFF_TIME); pw.print("=");
563 pw.print(MIN_LINEAR_BACKOFF_TIME); pw.println();
564
565 pw.print(" "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
566 pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
Christopher Tatea732f012017-10-26 17:26:53 -0700567
568 pw.print(" "); pw.print(KEY_STANDBY_HEARTBEAT_TIME); pw.print("=");
569 pw.print(STANDBY_HEARTBEAT_TIME); pw.println();
570
571 pw.print(" standby_beats={");
572 pw.print(STANDBY_BEATS[0]);
573 for (int i = 1; i < STANDBY_BEATS.length; i++) {
574 pw.print(", ");
575 pw.print(STANDBY_BEATS[i]);
576 }
577 pw.println('}');
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700578 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -0800579
580 void dump(ProtoOutputStream proto, long fieldId) {
581 final long token = proto.start(fieldId);
582
583 proto.write(ConstantsProto.MIN_IDLE_COUNT, MIN_IDLE_COUNT);
584 proto.write(ConstantsProto.MIN_CHARGING_COUNT, MIN_CHARGING_COUNT);
585 proto.write(ConstantsProto.MIN_BATTERY_NOT_LOW_COUNT, MIN_BATTERY_NOT_LOW_COUNT);
586 proto.write(ConstantsProto.MIN_STORAGE_NOT_LOW_COUNT, MIN_STORAGE_NOT_LOW_COUNT);
587 proto.write(ConstantsProto.MIN_CONNECTIVITY_COUNT, MIN_CONNECTIVITY_COUNT);
588 proto.write(ConstantsProto.MIN_CONTENT_COUNT, MIN_CONTENT_COUNT);
589 proto.write(ConstantsProto.MIN_READY_JOBS_COUNT, MIN_READY_JOBS_COUNT);
590 proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
591 proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
592 proto.write(ConstantsProto.FG_JOB_COUNT, FG_JOB_COUNT);
593 proto.write(ConstantsProto.BG_NORMAL_JOB_COUNT, BG_NORMAL_JOB_COUNT);
594 proto.write(ConstantsProto.BG_MODERATE_JOB_COUNT, BG_MODERATE_JOB_COUNT);
595 proto.write(ConstantsProto.BG_LOW_JOB_COUNT, BG_LOW_JOB_COUNT);
596 proto.write(ConstantsProto.BG_CRITICAL_JOB_COUNT, BG_CRITICAL_JOB_COUNT);
597 proto.write(ConstantsProto.MAX_STANDARD_RESCHEDULE_COUNT, MAX_STANDARD_RESCHEDULE_COUNT);
598 proto.write(ConstantsProto.MAX_WORK_RESCHEDULE_COUNT, MAX_WORK_RESCHEDULE_COUNT);
599 proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME);
600 proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME);
601 proto.write(ConstantsProto.STANDBY_HEARTBEAT_TIME_MS, STANDBY_HEARTBEAT_TIME);
602
603 for (int period : STANDBY_BEATS) {
604 proto.write(ConstantsProto.STANDBY_BEATS, period);
605 }
606
607 proto.end(token);
608 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700609 }
610
611 final Constants mConstants;
612
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700613 static final Comparator<JobStatus> mEnqueueTimeComparator = (o1, o2) -> {
614 if (o1.enqueueTime < o2.enqueueTime) {
615 return -1;
616 }
617 return o1.enqueueTime > o2.enqueueTime ? 1 : 0;
618 };
619
620 static <T> void addOrderedItem(ArrayList<T> array, T newItem, Comparator<T> comparator) {
621 int where = Collections.binarySearch(array, newItem, comparator);
622 if (where < 0) {
623 where = ~where;
624 }
625 array.add(where, newItem);
626 }
627
Dianne Hackborne9a988c2016-05-27 17:59:40 -0700628 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700629 * Cleans up outstanding jobs when a package is removed. Even if it's being replaced later we
630 * still clean up. On reinstall the package will have a new uid.
631 */
632 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
633 @Override
634 public void onReceive(Context context, Intent intent) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700635 final String action = intent.getAction();
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700636 if (DEBUG) {
Christopher Tateee7805b2016-07-15 16:56:56 -0700637 Slog.d(TAG, "Receieved: " + action);
Dianne Hackborn2fefbcf2016-03-18 15:34:54 -0700638 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700639 final String pkgName = getPackageName(intent);
640 final int pkgUid = intent.getIntExtra(Intent.EXTRA_UID, -1);
641
Christopher Tateee7805b2016-07-15 16:56:56 -0700642 if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
Christopher Tateb5c07882016-05-26 17:11:09 -0700643 // Purge the app's jobs if the whole package was just disabled. When this is
644 // the case the component name will be a bare package name.
Christopher Tateb5c07882016-05-26 17:11:09 -0700645 if (pkgName != null && pkgUid != -1) {
646 final String[] changedComponents = intent.getStringArrayExtra(
647 Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
648 if (changedComponents != null) {
649 for (String component : changedComponents) {
650 if (component.equals(pkgName)) {
651 if (DEBUG) {
652 Slog.d(TAG, "Package state change: " + pkgName);
653 }
654 try {
655 final int userId = UserHandle.getUserId(pkgUid);
656 IPackageManager pm = AppGlobals.getPackageManager();
657 final int state = pm.getApplicationEnabledSetting(pkgName, userId);
658 if (state == COMPONENT_ENABLED_STATE_DISABLED
659 || state == COMPONENT_ENABLED_STATE_DISABLED_USER) {
660 if (DEBUG) {
661 Slog.d(TAG, "Removing jobs for package " + pkgName
662 + " in user " + userId);
663 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700664 cancelJobsForPackageAndUid(pkgName, pkgUid,
665 "app disabled");
Christopher Tateb5c07882016-05-26 17:11:09 -0700666 }
Christopher Tate652c5ad2016-10-05 14:45:46 -0700667 } catch (RemoteException|IllegalArgumentException e) {
668 /*
669 * IllegalArgumentException means that the package doesn't exist.
670 * This arises when PACKAGE_CHANGED broadcast delivery has lagged
671 * behind outright uninstall, so by the time we try to act it's gone.
672 * We don't need to act on this PACKAGE_CHANGED when this happens;
673 * we'll get a PACKAGE_REMOVED later and clean up then.
674 *
675 * RemoteException can't actually happen; the package manager is
676 * running in this same process.
677 */
678 }
Christopher Tateb5c07882016-05-26 17:11:09 -0700679 break;
680 }
681 }
682 }
683 } else {
684 Slog.w(TAG, "PACKAGE_CHANGED for " + pkgName + " / uid " + pkgUid);
685 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700686 } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Christopher Tateaad67a32014-10-20 16:29:20 -0700687 // If this is an outright uninstall rather than the first half of an
688 // app update sequence, cancel the jobs associated with the app.
689 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
690 int uidRemoved = intent.getIntExtra(Intent.EXTRA_UID, -1);
691 if (DEBUG) {
692 Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
693 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700694 cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
Christopher Tate7060b042014-06-09 19:50:00 -0700695 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700696 } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
Christopher Tate7060b042014-06-09 19:50:00 -0700697 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
698 if (DEBUG) {
699 Slog.d(TAG, "Removing jobs for user: " + userId);
700 }
701 cancelJobsForUser(userId);
Christopher Tateee7805b2016-07-15 16:56:56 -0700702 } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
703 // Has this package scheduled any jobs, such that we will take action
704 // if it were to be force-stopped?
Christopher Tateee7805b2016-07-15 16:56:56 -0700705 if (pkgUid != -1) {
706 List<JobStatus> jobsForUid;
707 synchronized (mLock) {
708 jobsForUid = mJobs.getJobsByUid(pkgUid);
709 }
710 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
711 if (jobsForUid.get(i).getSourcePackageName().equals(pkgName)) {
712 if (DEBUG) {
713 Slog.d(TAG, "Restart query: package " + pkgName + " at uid "
714 + pkgUid + " has jobs");
715 }
716 setResultCode(Activity.RESULT_OK);
717 break;
718 }
719 }
720 }
721 } else if (Intent.ACTION_PACKAGE_RESTARTED.equals(action)) {
722 // possible force-stop
Christopher Tateee7805b2016-07-15 16:56:56 -0700723 if (pkgUid != -1) {
724 if (DEBUG) {
725 Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
726 }
Makoto Onukie7b96182017-08-30 14:53:16 -0700727 cancelJobsForPackageAndUid(pkgName, pkgUid, "app force stopped");
Christopher Tateee7805b2016-07-15 16:56:56 -0700728 }
Christopher Tate7060b042014-06-09 19:50:00 -0700729 }
730 }
731 };
732
Christopher Tateb5c07882016-05-26 17:11:09 -0700733 private String getPackageName(Intent intent) {
734 Uri uri = intent.getData();
735 String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
736 return pkg;
737 }
738
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700739 final private IUidObserver mUidObserver = new IUidObserver.Stub() {
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700740 @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800741 updateUidState(uid, procState);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700742 }
743
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700744 @Override public void onUidGone(int uid, boolean disabled) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800745 updateUidState(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY);
Dianne Hackborne07641d2016-11-09 15:07:23 -0800746 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700747 cancelJobsForUid(uid, "uid gone");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800748 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700749 synchronized (mLock) {
750 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
751 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700752 }
753
754 @Override public void onUidActive(int uid) throws RemoteException {
Suprabh Shukla106203b2017-11-02 21:23:44 -0700755 synchronized (mLock) {
756 mDeviceIdleJobsController.setUidActiveLocked(uid, true);
757 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700758 }
759
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700760 @Override public void onUidIdle(int uid, boolean disabled) {
Dianne Hackborne07641d2016-11-09 15:07:23 -0800761 if (disabled) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700762 cancelJobsForUid(uid, "app uid idle");
Dianne Hackborne07641d2016-11-09 15:07:23 -0800763 }
Suprabh Shukla106203b2017-11-02 21:23:44 -0700764 synchronized (mLock) {
765 mDeviceIdleJobsController.setUidActiveLocked(uid, false);
766 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700767 }
Dianne Hackborn3e99f652017-07-05 16:33:56 -0700768
769 @Override public void onUidCachedChanged(int uid, boolean cached) {
770 }
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700771 };
772
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800773 public Object getLock() {
774 return mLock;
775 }
776
Dianne Hackborn8db0fc12016-04-12 13:48:25 -0700777 public JobStore getJobStore() {
778 return mJobs;
779 }
780
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700781 @Override
782 public void onStartUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700783 mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userHandle);
784 // Let's kick any outstanding jobs for this user.
785 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
786 }
787
788 @Override
789 public void onUnlockUser(int userHandle) {
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700790 // Let's kick any outstanding jobs for this user.
791 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
792 }
793
794 @Override
795 public void onStopUser(int userHandle) {
Jeff Sharkey822cbd12016-02-25 11:09:55 -0700796 mStartedUsers = ArrayUtils.removeInt(mStartedUsers, userHandle);
Matthew Williams9ae3dbe2014-08-21 13:47:47 -0700797 }
798
Makoto Onuki15407842018-01-19 14:23:11 -0800799 /**
Makoto Onukie4918212018-02-06 11:30:15 -0800800 * Return whether an UID is active or idle.
Makoto Onuki15407842018-01-19 14:23:11 -0800801 */
Makoto Onukie4918212018-02-06 11:30:15 -0800802 private boolean isUidActive(int uid) {
803 return mAppStateTracker.isUidActiveSynced(uid);
Makoto Onuki15407842018-01-19 14:23:11 -0800804 }
805
Makoto Onukie4918212018-02-06 11:30:15 -0800806 private final Predicate<Integer> mIsUidActivePredicate = this::isUidActive;
Makoto Onuki15407842018-01-19 14:23:11 -0800807
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700808 public int scheduleAsPackage(JobInfo job, JobWorkItem work, int uId, String packageName,
809 int userId, String tag) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700810 try {
Dianne Hackbornc3af19a2017-01-20 17:00:44 -0800811 if (ActivityManager.getService().isAppStartModeDisabled(uId,
812 job.getService().getPackageName())) {
Dianne Hackbornbef28fe2015-10-29 17:57:11 -0700813 Slog.w(TAG, "Not scheduling job " + uId + ":" + job.toString()
814 + " -- package not allowed to start");
815 return JobScheduler.RESULT_FAILURE;
816 }
817 } catch (RemoteException e) {
818 }
Christopher Tatea732f012017-10-26 17:26:53 -0700819
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800820 synchronized (mLock) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700821 final JobStatus toCancel = mJobs.getJobByUidAndJobId(uId, job.getId());
822
823 if (work != null && toCancel != null) {
824 // Fast path: we are adding work to an existing job, and the JobInfo is not
825 // changing. We can just directly enqueue this work in to the job.
826 if (toCancel.getJob().equals(job)) {
Makoto Onuki15407842018-01-19 14:23:11 -0800827
Dianne Hackborn342e6032017-04-13 18:04:31 -0700828 toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
Makoto Onuki15407842018-01-19 14:23:11 -0800829
830 // If any of work item is enqueued when the source is in the foreground,
831 // exempt the entire job.
Makoto Onukie4918212018-02-06 11:30:15 -0800832 toCancel.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800833
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700834 return JobScheduler.RESULT_SUCCESS;
835 }
836 }
837
838 JobStatus jobStatus = JobStatus.createFromJobInfo(job, uId, packageName, userId, tag);
Makoto Onuki15407842018-01-19 14:23:11 -0800839
840 // Give exemption if the source is in the foreground just now.
841 // Note if it's a sync job, this method is called on the handler so it's not exactly
842 // the state when requestSync() was called, but that should be fine because of the
843 // 1 minute foreground grace period.
Makoto Onukie4918212018-02-06 11:30:15 -0800844 jobStatus.maybeAddForegroundExemption(mIsUidActivePredicate);
Makoto Onuki15407842018-01-19 14:23:11 -0800845
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700846 if (DEBUG) Slog.d(TAG, "SCHEDULE: " + jobStatus.toShortString());
Christopher Tate2f36fd62016-02-18 18:36:08 -0800847 // Jobs on behalf of others don't apply to the per-app job cap
Christopher Tatedabdf6f2016-02-24 12:30:22 -0800848 if (ENFORCE_MAX_JOBS && packageName == null) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800849 if (mJobs.countJobsForUid(uId) > MAX_JOBS_PER_APP) {
850 Slog.w(TAG, "Too many jobs for uid " + uId);
851 throw new IllegalStateException("Apps may not schedule more than "
852 + MAX_JOBS_PER_APP + " distinct jobs");
853 }
854 }
855
Dianne Hackborna47223f2017-03-30 13:49:13 -0700856 // This may throw a SecurityException.
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700857 jobStatus.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -0700858
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700859 if (toCancel != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700860 cancelJobImplLocked(toCancel, jobStatus, "job rescheduled by app");
Christopher Tateb1c1f9a2016-03-17 13:29:25 -0700861 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700862 if (work != null) {
863 // If work has been supplied, enqueue it into the new job.
Dianne Hackborn342e6032017-04-13 18:04:31 -0700864 jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700865 }
866 startTrackingJobLocked(jobStatus, toCancel);
Tej Singhd5747a62018-01-08 20:57:35 -0800867 StatsLog.write_non_chained(StatsLog.SCHEDULED_JOB_STATE_CHANGED,
Bookatz90867622018-01-31 15:05:57 -0800868 uId, null, jobStatus.getBatteryName(),
869 StatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__SCHEDULED);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700870
871 // If the job is immediately ready to run, then we can just immediately
872 // put it in the pending list and try to schedule it. This is especially
873 // important for jobs with a 0 deadline constraint, since they will happen a fair
874 // amount, we want to handle them as quickly as possible, and semantically we want to
875 // make sure we have started holding the wake lock for the job before returning to
876 // the caller.
877 // If the job is not yet ready to run, there is nothing more to do -- we are
878 // now just waiting for one of its controllers to change state and schedule
879 // the job appropriately.
880 if (isReadyToBeExecutedLocked(jobStatus)) {
881 // This is a new job, we can just immediately put it on the pending
882 // list and try to run it.
883 mJobPackageTracker.notePending(jobStatus);
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700884 addOrderedItem(mPendingJobs, jobStatus, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700885 maybeRunPendingJobsLocked();
886 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800887 }
Christopher Tate7060b042014-06-09 19:50:00 -0700888 return JobScheduler.RESULT_SUCCESS;
889 }
890
891 public List<JobInfo> getPendingJobs(int uid) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800892 synchronized (mLock) {
Christopher Tate2f36fd62016-02-18 18:36:08 -0800893 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
894 ArrayList<JobInfo> outList = new ArrayList<JobInfo>(jobs.size());
895 for (int i = jobs.size() - 1; i >= 0; i--) {
896 JobStatus job = jobs.get(i);
897 outList.add(job.getJob());
Christopher Tate7060b042014-06-09 19:50:00 -0700898 }
Christopher Tate2f36fd62016-02-18 18:36:08 -0800899 return outList;
Christopher Tate7060b042014-06-09 19:50:00 -0700900 }
Christopher Tate7060b042014-06-09 19:50:00 -0700901 }
902
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -0600903 public JobInfo getPendingJob(int uid, int jobId) {
904 synchronized (mLock) {
905 List<JobStatus> jobs = mJobs.getJobsByUid(uid);
906 for (int i = jobs.size() - 1; i >= 0; i--) {
907 JobStatus job = jobs.get(i);
908 if (job.getJobId() == jobId) {
909 return job.getJob();
910 }
911 }
912 return null;
913 }
914 }
915
Dianne Hackborn88e98df2015-03-23 13:29:14 -0700916 void cancelJobsForUser(int userHandle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800917 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700918 final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
919 for (int i=0; i<jobsForUser.size(); i++) {
920 JobStatus toRemove = jobsForUser.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700921 cancelJobImplLocked(toRemove, null, "user removed");
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700922 }
Christopher Tate7060b042014-06-09 19:50:00 -0700923 }
924 }
925
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -0700926 private void cancelJobsForNonExistentUsers() {
927 UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
928 synchronized (mLock) {
929 mJobs.removeJobsOfNonUsers(umi.getUserIds());
930 }
931 }
932
Makoto Onukie7b96182017-08-30 14:53:16 -0700933 void cancelJobsForPackageAndUid(String pkgName, int uid, String reason) {
934 if ("android".equals(pkgName)) {
935 Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
936 return;
937 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700938 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700939 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
940 for (int i = jobsForUid.size() - 1; i >= 0; i--) {
941 final JobStatus job = jobsForUid.get(i);
942 if (job.getSourcePackageName().equals(pkgName)) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700943 cancelJobImplLocked(job, null, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700944 }
Christopher Tateee7805b2016-07-15 16:56:56 -0700945 }
946 }
947 }
948
Christopher Tate7060b042014-06-09 19:50:00 -0700949 /**
950 * Entry point from client to cancel all jobs originating from their uid.
951 * This will remove the job from the master list, and cancel the job if it was staged for
952 * execution or being executed.
Matthew Williams48a30db2014-09-23 13:39:36 -0700953 * @param uid Uid to check against for removal of a job.
Dianne Hackborne07641d2016-11-09 15:07:23 -0800954 *
Christopher Tate7060b042014-06-09 19:50:00 -0700955 */
Christopher Tate8c67d122017-09-29 16:54:26 -0700956 public boolean cancelJobsForUid(int uid, String reason) {
Makoto Onukie7b02982017-08-24 14:23:36 -0700957 if (uid == Process.SYSTEM_UID) {
Makoto Onukie7b96182017-08-30 14:53:16 -0700958 Slog.wtfStack(TAG, "Can't cancel all jobs for system uid");
Christopher Tate8c67d122017-09-29 16:54:26 -0700959 return false;
Makoto Onukie7b02982017-08-24 14:23:36 -0700960 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700961
962 boolean jobsCanceled = false;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800963 synchronized (mLock) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700964 final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
965 for (int i=0; i<jobsForUid.size(); i++) {
966 JobStatus toRemove = jobsForUid.get(i);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700967 cancelJobImplLocked(toRemove, null, reason);
Christopher Tate8c67d122017-09-29 16:54:26 -0700968 jobsCanceled = true;
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700969 }
Christopher Tate7060b042014-06-09 19:50:00 -0700970 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700971 return jobsCanceled;
Christopher Tate7060b042014-06-09 19:50:00 -0700972 }
973
974 /**
975 * Entry point from client to cancel the job corresponding to the jobId provided.
976 * This will remove the job from the master list, and cancel the job if it was staged for
977 * execution or being executed.
978 * @param uid Uid of the calling client.
979 * @param jobId Id of the job, provided at schedule-time.
980 */
Makoto Onukid2bfec62018-01-12 13:58:01 -0800981 public boolean cancelJob(int uid, int jobId, int callingUid) {
Christopher Tate7060b042014-06-09 19:50:00 -0700982 JobStatus toCancel;
Dianne Hackborn33d31c52016-02-16 10:30:33 -0800983 synchronized (mLock) {
Christopher Tate7060b042014-06-09 19:50:00 -0700984 toCancel = mJobs.getJobByUidAndJobId(uid, jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700985 if (toCancel != null) {
Makoto Onukid2bfec62018-01-12 13:58:01 -0800986 cancelJobImplLocked(toCancel, null,
987 "cancel() called by app, callingUid=" + callingUid
988 + " uid=" + uid + " jobId=" + jobId);
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700989 }
Christopher Tate8c67d122017-09-29 16:54:26 -0700990 return (toCancel != null);
Christopher Tate7060b042014-06-09 19:50:00 -0700991 }
992 }
993
Dianne Hackborn729a3282017-06-09 16:06:01 -0700994 private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -0700995 if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
996 cancelled.unprepareLocked(ActivityManager.getService());
997 stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
998 // Remove from pending queue.
999 if (mPendingJobs.remove(cancelled)) {
1000 mJobPackageTracker.noteNonpending(cancelled);
Matthew Williams48a30db2014-09-23 13:39:36 -07001001 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001002 // Cancel if running.
Dianne Hackborn729a3282017-06-09 16:06:01 -07001003 stopJobOnServiceContextLocked(cancelled, JobParameters.REASON_CANCELED, reason);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001004 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001005 }
1006
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001007 void updateUidState(int uid, int procState) {
1008 synchronized (mLock) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001009 if (procState == ActivityManager.PROCESS_STATE_TOP) {
1010 // Only use this if we are exactly the top app. All others can live
1011 // with just the foreground priority. This means that persistent processes
1012 // can never be the top app priority... that is fine.
1013 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_TOP_APP);
Dianne Hackborn10fc4fd2017-12-19 17:23:13 -08001014 } else if (procState <= ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001015 mUidPriorityOverride.put(uid, JobInfo.PRIORITY_FOREGROUND_APP);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001016 } else {
Dianne Hackborn970510b2016-02-24 16:56:42 -08001017 mUidPriorityOverride.delete(uid);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001018 }
1019 }
1020 }
1021
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001022 @Override
1023 public void onDeviceIdleStateChanged(boolean deviceIdle) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001024 synchronized (mLock) {
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001025 if (deviceIdle) {
Jeff Sharkey34618b52016-06-01 15:51:19 -06001026 // When becoming idle, make sure no jobs are actively running,
1027 // except those using the idle exemption flag.
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001028 for (int i=0; i<mActiveServices.size(); i++) {
1029 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001030 final JobStatus executing = jsc.getRunningJobLocked();
Jeff Sharkey34618b52016-06-01 15:51:19 -06001031 if (executing != null
1032 && (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001033 jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE,
1034 "cancelled due to doze");
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001035 }
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001036 }
Amith Yamasanicb926fc2016-03-14 17:15:20 -07001037 } else {
1038 // When coming out of idle, allow thing to start back up.
1039 if (mReadyToRock) {
1040 if (mLocalDeviceIdleController != null) {
1041 if (!mReportedActive) {
1042 mReportedActive = true;
1043 mLocalDeviceIdleController.setJobsActive(true);
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001044 }
1045 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001046 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Dianne Hackborn88e98df2015-03-23 13:29:14 -07001047 }
1048 }
1049 }
1050 }
1051
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001052 void reportActiveLocked() {
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001053 // active is true if pending queue contains jobs OR some job is running.
1054 boolean active = mPendingJobs.size() > 0;
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001055 if (mPendingJobs.size() <= 0) {
1056 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001057 final JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001058 final JobStatus job = jsc.getRunningJobLocked();
Dianne Hackborn7ab40252016-06-15 17:30:24 -07001059 if (job != null
1060 && (job.getJob().getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0
1061 && !job.dozeWhitelisted) {
1062 // We will report active if we have a job running and it is not an exception
1063 // due to being in the foreground or whitelisted.
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001064 active = true;
1065 break;
1066 }
1067 }
1068 }
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001069
1070 if (mReportedActive != active) {
1071 mReportedActive = active;
1072 if (mLocalDeviceIdleController != null) {
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001073 mLocalDeviceIdleController.setJobsActive(active);
1074 }
1075 }
1076 }
1077
Christopher Tated117b292018-01-05 17:32:36 -08001078 void reportAppUsage(String packageName, int userId) {
1079 // This app just transitioned into interactive use or near equivalent, so we should
1080 // take a look at its job state for feedback purposes.
1081 }
1082
Christopher Tate7060b042014-06-09 19:50:00 -07001083 /**
1084 * Initializes the system service.
1085 * <p>
1086 * Subclasses must define a single argument constructor that accepts the context
1087 * and passes it to super.
1088 * </p>
1089 *
1090 * @param context The system server context.
1091 */
1092 public JobSchedulerService(Context context) {
1093 super(context);
Christopher Tatea732f012017-10-26 17:26:53 -07001094
1095 mLocalPM = LocalServices.getService(PackageManagerInternal.class);
Makoto Onuki15407842018-01-19 14:23:11 -08001096 mActivityManagerInternal = Preconditions.checkNotNull(
1097 LocalServices.getService(ActivityManagerInternal.class));
Christopher Tatea732f012017-10-26 17:26:53 -07001098
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001099 mHandler = new JobHandler(context.getMainLooper());
1100 mConstants = new Constants(mHandler);
1101 mJobSchedulerStub = new JobSchedulerStub();
Christopher Tatea732f012017-10-26 17:26:53 -07001102
1103 // Set up the app standby bucketing tracker
Christopher Tated1aebb32018-01-31 13:24:14 -08001104 mStandbyTracker = new StandbyTracker();
1105 mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
1106 mUsageStats.addAppIdleStateChangeListener(mStandbyTracker);
Christopher Tatea732f012017-10-26 17:26:53 -07001107
1108 // The job store needs to call back
1109 publishLocalService(JobSchedulerInternal.class, new LocalService());
1110
1111 // Initialize the job store and set up any persisted jobs
Dianne Hackborn970e3f42016-06-01 10:55:13 -07001112 mJobs = JobStore.initAndGet(this);
1113
Christopher Tate7060b042014-06-09 19:50:00 -07001114 // Create the controllers.
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001115 mControllers = new ArrayList<StateController>();
Christopher Tate7060b042014-06-09 19:50:00 -07001116 mControllers.add(ConnectivityController.get(this));
1117 mControllers.add(TimeController.get(this));
1118 mControllers.add(IdleController.get(this));
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08001119 mBatteryController = BatteryController.get(this);
1120 mControllers.add(mBatteryController);
Dianne Hackborn532ea262017-03-17 17:50:55 -07001121 mStorageController = StorageController.get(this);
1122 mControllers.add(mStorageController);
Suprabh Shukla106203b2017-11-02 21:23:44 -07001123 mControllers.add(BackgroundJobsController.get(this));
Amith Yamasanib0ff3222015-03-04 09:56:14 -08001124 mControllers.add(AppIdleController.get(this));
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001125 mControllers.add(ContentObserverController.get(this));
Suprabh Shukla106203b2017-11-02 21:23:44 -07001126 mDeviceIdleJobsController = DeviceIdleJobsController.get(this);
1127 mControllers.add(mDeviceIdleJobsController);
Christopher Tate616541d2017-07-26 14:27:38 -07001128
1129 // If the job store determined that it can't yet reschedule persisted jobs,
1130 // we need to start watching the clock.
1131 if (!mJobs.jobTimesInflatedValid()) {
1132 Slog.w(TAG, "!!! RTC not yet good; tracking time updates for job scheduling");
1133 context.registerReceiver(mTimeSetReceiver, new IntentFilter(Intent.ACTION_TIME_CHANGED));
1134 }
Christopher Tate7060b042014-06-09 19:50:00 -07001135 }
1136
Christopher Tate616541d2017-07-26 14:27:38 -07001137 private final BroadcastReceiver mTimeSetReceiver = new BroadcastReceiver() {
1138 @Override
1139 public void onReceive(Context context, Intent intent) {
1140 if (Intent.ACTION_TIME_CHANGED.equals(intent.getAction())) {
1141 // When we reach clock sanity, recalculate the temporal windows
1142 // of all affected jobs.
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001143 if (mJobs.clockNowValidToInflate(sSystemClock.millis())) {
Christopher Tate616541d2017-07-26 14:27:38 -07001144 Slog.i(TAG, "RTC now valid; recalculating persisted job windows");
1145
1146 // We've done our job now, so stop watching the time.
1147 context.unregisterReceiver(this);
1148
1149 // And kick off the work to update the affected jobs, using a secondary
1150 // thread instead of chugging away here on the main looper thread.
1151 FgThread.getHandler().post(mJobTimeUpdater);
1152 }
1153 }
1154 }
1155 };
1156
1157 private final Runnable mJobTimeUpdater = () -> {
1158 final ArrayList<JobStatus> toRemove = new ArrayList<>();
1159 final ArrayList<JobStatus> toAdd = new ArrayList<>();
1160 synchronized (mLock) {
1161 // Note: we intentionally both look up the existing affected jobs and replace them
1162 // with recalculated ones inside the same lock lifetime.
1163 getJobStore().getRtcCorrectedJobsLocked(toAdd, toRemove);
1164
1165 // Now, at each position [i], we have both the existing JobStatus
1166 // and the one that replaces it.
1167 final int N = toAdd.size();
1168 for (int i = 0; i < N; i++) {
1169 final JobStatus oldJob = toRemove.get(i);
1170 final JobStatus newJob = toAdd.get(i);
1171 if (DEBUG) {
1172 Slog.v(TAG, " replacing " + oldJob + " with " + newJob);
1173 }
1174 cancelJobImplLocked(oldJob, newJob, "deferred rtc calculation");
1175 }
1176 }
1177 };
1178
Christopher Tate7060b042014-06-09 19:50:00 -07001179 @Override
1180 public void onStart() {
1181 publishBinderService(Context.JOB_SCHEDULER_SERVICE, mJobSchedulerStub);
1182 }
1183
1184 @Override
1185 public void onBootPhase(int phase) {
1186 if (PHASE_SYSTEM_SERVICES_READY == phase) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001187 mConstants.start(getContext().getContentResolver());
Makoto Onuki15407842018-01-19 14:23:11 -08001188
Makoto Onukie4918212018-02-06 11:30:15 -08001189 mAppStateTracker = Preconditions.checkNotNull(
1190 LocalServices.getService(AppStateTracker.class));
Christopher Tated1aebb32018-01-31 13:24:14 -08001191 setNextHeartbeatAlarm();
Makoto Onuki15407842018-01-19 14:23:11 -08001192
Shreyas Basarge5db09082016-01-07 13:38:29 +00001193 // Register br for package removals and user removals.
Christopher Tateb5c07882016-05-26 17:11:09 -07001194 final IntentFilter filter = new IntentFilter();
1195 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1196 filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
Christopher Tateee7805b2016-07-15 16:56:56 -07001197 filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
1198 filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
Christopher Tate7060b042014-06-09 19:50:00 -07001199 filter.addDataScheme("package");
1200 getContext().registerReceiverAsUser(
1201 mBroadcastReceiver, UserHandle.ALL, filter, null, null);
1202 final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
1203 getContext().registerReceiverAsUser(
1204 mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001205 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001206 ActivityManager.getService().registerUidObserver(mUidObserver,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001207 ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
Suprabh Shukla3ac1daa2017-07-14 12:15:27 -07001208 | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
1209 ActivityManager.PROCESS_STATE_UNKNOWN, null);
Dianne Hackbornbef28fe2015-10-29 17:57:11 -07001210 } catch (RemoteException e) {
1211 // ignored; both services live in system_server
1212 }
Michael Wachenschwanz3f2b6552017-05-15 13:53:09 -07001213 // Remove any jobs that are not associated with any of the current users.
1214 cancelJobsForNonExistentUsers();
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001215 } else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001216 synchronized (mLock) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001217 // Let's go!
1218 mReadyToRock = true;
1219 mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
1220 BatteryStats.SERVICE_NAME));
Dianne Hackborn627dfa12015-11-11 18:10:30 -08001221 mLocalDeviceIdleController
1222 = LocalServices.getService(DeviceIdleController.LocalService.class);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001223 // Create the "runners".
1224 for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
1225 mActiveServices.add(
Dianne Hackborn807de782016-04-07 17:54:41 -07001226 new JobServiceContext(this, mBatteryStats, mJobPackageTracker,
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001227 getContext().getMainLooper()));
1228 }
1229 // Attach jobs to their controllers.
Christopher Tate2f36fd62016-02-18 18:36:08 -08001230 mJobs.forEachJob(new JobStatusFunctor() {
1231 @Override
1232 public void process(JobStatus job) {
1233 for (int controller = 0; controller < mControllers.size(); controller++) {
1234 final StateController sc = mControllers.get(controller);
Christopher Tate2f36fd62016-02-18 18:36:08 -08001235 sc.maybeStartTrackingJobLocked(job, null);
1236 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001237 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001238 });
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001239 // GO GO GO!
1240 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1241 }
Christopher Tate7060b042014-06-09 19:50:00 -07001242 }
1243 }
1244
1245 /**
1246 * Called when we have a job status object that we need to insert in our
1247 * {@link com.android.server.job.JobStore}, and make sure all the relevant controllers know
1248 * about.
1249 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001250 private void startTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
1251 if (!jobStatus.isPreparedLocked()) {
1252 Slog.wtf(TAG, "Not yet prepared when started tracking: " + jobStatus);
1253 }
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001254 jobStatus.enqueueTime = sElapsedRealtimeClock.millis();
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001255 final boolean update = mJobs.add(jobStatus);
1256 if (mReadyToRock) {
1257 for (int i = 0; i < mControllers.size(); i++) {
1258 StateController controller = mControllers.get(i);
1259 if (update) {
1260 controller.maybeStopTrackingJobLocked(jobStatus, null, true);
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001261 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001262 controller.maybeStartTrackingJobLocked(jobStatus, lastJob);
Christopher Tate7060b042014-06-09 19:50:00 -07001263 }
Christopher Tate7060b042014-06-09 19:50:00 -07001264 }
1265 }
1266
1267 /**
1268 * Called when we want to remove a JobStatus object that we've finished executing. Returns the
1269 * object removed.
1270 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001271 private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
Dianne Hackborn141f11c2016-04-05 15:46:12 -07001272 boolean writeBack) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001273 // Deal with any remaining work items in the old job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07001274 jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001275
1276 // Remove from store as well as controllers.
1277 final boolean removed = mJobs.remove(jobStatus, writeBack);
1278 if (removed && mReadyToRock) {
1279 for (int i=0; i<mControllers.size(); i++) {
1280 StateController controller = mControllers.get(i);
1281 controller.maybeStopTrackingJobLocked(jobStatus, incomingJob, false);
Christopher Tate7060b042014-06-09 19:50:00 -07001282 }
1283 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001284 return removed;
Christopher Tate7060b042014-06-09 19:50:00 -07001285 }
1286
Dianne Hackborn729a3282017-06-09 16:06:01 -07001287 private boolean stopJobOnServiceContextLocked(JobStatus job, int reason, String debugReason) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001288 for (int i=0; i<mActiveServices.size(); i++) {
1289 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001290 final JobStatus executing = jsc.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001291 if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07001292 jsc.cancelExecutingJobLocked(reason, debugReason);
Christopher Tate7060b042014-06-09 19:50:00 -07001293 return true;
1294 }
1295 }
1296 return false;
1297 }
1298
1299 /**
1300 * @param job JobStatus we are querying against.
1301 * @return Whether or not the job represented by the status object is currently being run or
1302 * is pending.
1303 */
1304 private boolean isCurrentlyActiveLocked(JobStatus job) {
Dianne Hackbornfdb19562014-07-11 16:03:36 -07001305 for (int i=0; i<mActiveServices.size(); i++) {
1306 JobServiceContext serviceContext = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001307 final JobStatus running = serviceContext.getRunningJobLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001308 if (running != null && running.matches(job.getUid(), job.getJobId())) {
1309 return true;
1310 }
1311 }
1312 return false;
1313 }
1314
Dianne Hackborn807de782016-04-07 17:54:41 -07001315 void noteJobsPending(List<JobStatus> jobs) {
1316 for (int i = jobs.size() - 1; i >= 0; i--) {
1317 JobStatus job = jobs.get(i);
1318 mJobPackageTracker.notePending(job);
1319 }
1320 }
1321
1322 void noteJobsNonpending(List<JobStatus> jobs) {
1323 for (int i = jobs.size() - 1; i >= 0; i--) {
1324 JobStatus job = jobs.get(i);
1325 mJobPackageTracker.noteNonpending(job);
1326 }
1327 }
1328
Christopher Tate7060b042014-06-09 19:50:00 -07001329 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001330 * Reschedules the given job based on the job's backoff policy. It doesn't make sense to
1331 * specify an override deadline on a failed job (the failed job will run even though it's not
1332 * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any
1333 * ready job with {@link JobStatus#numFailures} > 0 will be executed.
1334 *
Christopher Tate7060b042014-06-09 19:50:00 -07001335 * @param failureToReschedule Provided job status that we will reschedule.
1336 * @return A newly instantiated JobStatus with the same constraints as the last job except
1337 * with adjusted timing constraints.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001338 *
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001339 * @see #maybeQueueReadyJobsForExecutionLocked
Christopher Tate7060b042014-06-09 19:50:00 -07001340 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001341 private JobStatus getRescheduleJobForFailureLocked(JobStatus failureToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001342 final long elapsedNowMillis = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001343 final JobInfo job = failureToReschedule.getJob();
1344
1345 final long initialBackoffMillis = job.getInitialBackoffMillis();
Matthew Williamsd1c06752014-08-22 14:15:28 -07001346 final int backoffAttempts = failureToReschedule.getNumFailures() + 1;
1347 long delayMillis;
Christopher Tate7060b042014-06-09 19:50:00 -07001348
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001349 if (failureToReschedule.hasWorkLocked()) {
1350 if (backoffAttempts > mConstants.MAX_WORK_RESCHEDULE_COUNT) {
1351 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1352 + backoffAttempts + " > work limit "
1353 + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1354 return null;
1355 }
1356 } else if (backoffAttempts > mConstants.MAX_STANDARD_RESCHEDULE_COUNT) {
1357 Slog.w(TAG, "Not rescheduling " + failureToReschedule + ": attempt #"
1358 + backoffAttempts + " > std limit " + mConstants.MAX_STANDARD_RESCHEDULE_COUNT);
1359 return null;
1360 }
1361
Christopher Tate7060b042014-06-09 19:50:00 -07001362 switch (job.getBackoffPolicy()) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001363 case JobInfo.BACKOFF_POLICY_LINEAR: {
1364 long backoff = initialBackoffMillis;
1365 if (backoff < mConstants.MIN_LINEAR_BACKOFF_TIME) {
1366 backoff = mConstants.MIN_LINEAR_BACKOFF_TIME;
1367 }
1368 delayMillis = backoff * backoffAttempts;
1369 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001370 default:
1371 if (DEBUG) {
1372 Slog.v(TAG, "Unrecognised back-off policy, defaulting to exponential.");
1373 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001374 case JobInfo.BACKOFF_POLICY_EXPONENTIAL: {
1375 long backoff = initialBackoffMillis;
1376 if (backoff < mConstants.MIN_EXP_BACKOFF_TIME) {
1377 backoff = mConstants.MIN_EXP_BACKOFF_TIME;
1378 }
1379 delayMillis = (long) Math.scalb(backoff, backoffAttempts - 1);
1380 } break;
Christopher Tate7060b042014-06-09 19:50:00 -07001381 }
Matthew Williamsd1c06752014-08-22 14:15:28 -07001382 delayMillis =
1383 Math.min(delayMillis, JobInfo.MAX_BACKOFF_DELAY_MILLIS);
Christopher Tatea732f012017-10-26 17:26:53 -07001384 JobStatus newJob = new JobStatus(failureToReschedule, getCurrentHeartbeat(),
1385 elapsedNowMillis + delayMillis,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001386 JobStatus.NO_LATEST_RUNTIME, backoffAttempts,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001387 failureToReschedule.getLastSuccessfulRunTime(), sSystemClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001388 for (int ic=0; ic<mControllers.size(); ic++) {
1389 StateController controller = mControllers.get(ic);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001390 controller.rescheduleForFailureLocked(newJob, failureToReschedule);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001391 }
1392 return newJob;
Christopher Tate7060b042014-06-09 19:50:00 -07001393 }
1394
1395 /**
Matthew Williams1bde39a2015-10-07 14:29:30 -07001396 * Called after a periodic has executed so we can reschedule it. We take the last execution
1397 * time of the job to be the time of completion (i.e. the time at which this function is
1398 * called).
Christopher Tatea732f012017-10-26 17:26:53 -07001399 * <p>This could be inaccurate b/c the job can run for as long as
Christopher Tate7060b042014-06-09 19:50:00 -07001400 * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead
1401 * to underscheduling at least, rather than if we had taken the last execution time to be the
1402 * start of the execution.
Christopher Tatea732f012017-10-26 17:26:53 -07001403 * <p>Unlike a reschedule prior to execution, in this case we advance the next-heartbeat
1404 * tracking as though the job were newly-scheduled.
Christopher Tate7060b042014-06-09 19:50:00 -07001405 * @return A new job representing the execution criteria for this instantiation of the
1406 * recurring job.
1407 */
1408 private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) {
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001409 final long elapsedNow = sElapsedRealtimeClock.millis();
Christopher Tate7060b042014-06-09 19:50:00 -07001410 // Compute how much of the period is remaining.
Matthew Williams1bde39a2015-10-07 14:29:30 -07001411 long runEarly = 0L;
1412
1413 // If this periodic was rescheduled it won't have a deadline.
1414 if (periodicToReschedule.hasDeadlineConstraint()) {
1415 runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L);
1416 }
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001417 long flex = periodicToReschedule.getJob().getFlexMillis();
Christopher Tate7060b042014-06-09 19:50:00 -07001418 long period = periodicToReschedule.getJob().getIntervalMillis();
Shreyas Basarge89ee6182015-12-17 15:16:36 +00001419 long newLatestRuntimeElapsed = elapsedNow + runEarly + period;
1420 long newEarliestRunTimeElapsed = newLatestRuntimeElapsed - flex;
Christopher Tate7060b042014-06-09 19:50:00 -07001421
1422 if (DEBUG) {
1423 Slog.v(TAG, "Rescheduling executed periodic. New execution window [" +
1424 newEarliestRunTimeElapsed/1000 + ", " + newLatestRuntimeElapsed/1000 + "]s");
1425 }
Christopher Tatea732f012017-10-26 17:26:53 -07001426 return new JobStatus(periodicToReschedule, getCurrentHeartbeat(),
1427 newEarliestRunTimeElapsed, newLatestRuntimeElapsed,
1428 0 /* backoffAttempt */,
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07001429 sSystemClock.millis() /* lastSuccessfulRunTime */,
Makoto Onukiab8a67f2017-06-20 12:20:34 -07001430 periodicToReschedule.getLastFailedRunTime());
Christopher Tate7060b042014-06-09 19:50:00 -07001431 }
1432
Christopher Tated1aebb32018-01-31 13:24:14 -08001433 long heartbeatWhenJobsLastRun(String packageName, final @UserIdInt int userId) {
1434 final long heartbeat;
1435 final long timeSinceLastJob = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
1436 synchronized (mLock) {
1437 heartbeat = mHeartbeat - (timeSinceLastJob / mConstants.STANDBY_HEARTBEAT_TIME);
1438 }
1439 if (DEBUG_STANDBY) {
1440 Slog.v(TAG, "Last job heartbeat " + heartbeat + " for " + packageName + "/" + userId
1441 + " delta=" + timeSinceLastJob);
1442 }
1443 return heartbeat;
1444 }
1445
1446 long heartbeatWhenJobsLastRun(JobStatus job) {
1447 return heartbeatWhenJobsLastRun(job.getSourcePackageName(), job.getSourceUserId());
1448 }
1449
Christopher Tate7060b042014-06-09 19:50:00 -07001450 // JobCompletedListener implementations.
1451
1452 /**
1453 * A job just finished executing. We fetch the
1454 * {@link com.android.server.job.controllers.JobStatus} from the store and depending on
1455 * whether we want to reschedule we readd it to the controllers.
1456 * @param jobStatus Completed job.
1457 * @param needsReschedule Whether the implementing class should reschedule this job.
1458 */
1459 @Override
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001460 public void onJobCompletedLocked(JobStatus jobStatus, boolean needsReschedule) {
Christopher Tate7060b042014-06-09 19:50:00 -07001461 if (DEBUG) {
1462 Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
1463 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001464
1465 // If the job wants to be rescheduled, we first need to make the next upcoming
1466 // job so we can transfer any appropriate state over from the previous job when
1467 // we stop it.
1468 final JobStatus rescheduledJob = needsReschedule
1469 ? getRescheduleJobForFailureLocked(jobStatus) : null;
1470
Shreyas Basarge73f10252016-02-11 17:06:13 +00001471 // Do not write back immediately if this is a periodic job. The job may get lost if system
1472 // shuts down before it is added back.
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001473 if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
Christopher Tate7060b042014-06-09 19:50:00 -07001474 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07001475 Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
Christopher Tate7060b042014-06-09 19:50:00 -07001476 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08001477 // We still want to check for jobs to execute, because this job may have
1478 // scheduled a new job under the same job id, and now we can run it.
1479 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001480 return;
1481 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001482
1483 if (rescheduledJob != null) {
Dianne Hackborna47223f2017-03-30 13:49:13 -07001484 try {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001485 rescheduledJob.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001486 } catch (SecurityException e) {
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001487 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001488 }
Dianne Hackbornfd8807a2017-04-17 13:34:51 -07001489 startTrackingJobLocked(rescheduledJob, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001490 } else if (jobStatus.getJob().isPeriodic()) {
1491 JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
Dianne Hackborna47223f2017-03-30 13:49:13 -07001492 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001493 rescheduledPeriodic.prepareLocked(ActivityManager.getService());
Dianne Hackborna47223f2017-03-30 13:49:13 -07001494 } catch (SecurityException e) {
1495 Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
1496 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001497 startTrackingJobLocked(rescheduledPeriodic, jobStatus);
Christopher Tate7060b042014-06-09 19:50:00 -07001498 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07001499 jobStatus.unprepareLocked(ActivityManager.getService());
1500 reportActiveLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001501 mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001502 }
1503
1504 // StateChangedListener implementations.
1505
1506 /**
Matthew Williams48a30db2014-09-23 13:39:36 -07001507 * Posts a message to the {@link com.android.server.job.JobSchedulerService.JobHandler} that
1508 * some controller's state has changed, so as to run through the list of jobs and start/stop
1509 * any that are eligible.
Christopher Tate7060b042014-06-09 19:50:00 -07001510 */
1511 @Override
1512 public void onControllerStateChanged() {
Matthew Williams48a30db2014-09-23 13:39:36 -07001513 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
Christopher Tate7060b042014-06-09 19:50:00 -07001514 }
1515
1516 @Override
1517 public void onRunJobNow(JobStatus jobStatus) {
1518 mHandler.obtainMessage(MSG_JOB_EXPIRED, jobStatus).sendToTarget();
1519 }
1520
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001521 final private class JobHandler extends Handler {
Christopher Tate7060b042014-06-09 19:50:00 -07001522
1523 public JobHandler(Looper looper) {
1524 super(looper);
1525 }
1526
1527 @Override
1528 public void handleMessage(Message message) {
Dianne Hackborn33d31c52016-02-16 10:30:33 -08001529 synchronized (mLock) {
Matthew Williams48a30db2014-09-23 13:39:36 -07001530 if (!mReadyToRock) {
1531 return;
1532 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001533 switch (message.what) {
1534 case MSG_JOB_EXPIRED: {
Christopher Tate7060b042014-06-09 19:50:00 -07001535 JobStatus runNow = (JobStatus) message.obj;
Matthew Williamsbafeeb92014-08-08 11:51:06 -07001536 // runNow can be null, which is a controller's way of indicating that its
1537 // state is such that all ready jobs should be run immediately.
Christopher Tateb1d64482017-03-15 12:01:22 -07001538 if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001539 mJobPackageTracker.notePending(runNow);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001540 addOrderedItem(mPendingJobs, runNow, mEnqueueTimeComparator);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001541 } else {
1542 queueReadyJobsForExecutionLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001543 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001544 } break;
1545 case MSG_CHECK_JOB:
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001546 if (mReportedActive) {
1547 // if jobs are currently being run, queue all ready jobs for execution.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001548 queueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001549 } else {
1550 // Check the list of jobs and run some of them if we feel inclined.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001551 maybeQueueReadyJobsForExecutionLocked();
Shreyas Basarge4cff8ac2015-12-10 21:32:52 +00001552 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001553 break;
1554 case MSG_CHECK_JOB_GREEDY:
1555 queueReadyJobsForExecutionLocked();
1556 break;
1557 case MSG_STOP_JOB:
Dianne Hackborn729a3282017-06-09 16:06:01 -07001558 cancelJobImplLocked((JobStatus) message.obj, null,
1559 "app no longer allowed to run");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001560 break;
Matthew Williams75fc5252014-09-02 16:17:53 -07001561 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001562 maybeRunPendingJobsLocked();
1563 // Don't remove JOB_EXPIRED in case one came along while processing the queue.
1564 removeMessages(MSG_CHECK_JOB);
Christopher Tate7060b042014-06-09 19:50:00 -07001565 }
1566 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001567 }
Christopher Tate7060b042014-06-09 19:50:00 -07001568
Dianne Hackborn6d068262017-05-16 13:14:37 -07001569 private void stopNonReadyActiveJobsLocked() {
1570 for (int i=0; i<mActiveServices.size(); i++) {
1571 JobServiceContext serviceContext = mActiveServices.get(i);
1572 final JobStatus running = serviceContext.getRunningJobLocked();
1573 if (running != null && !running.isReady()) {
1574 serviceContext.cancelExecutingJobLocked(
Dianne Hackborn729a3282017-06-09 16:06:01 -07001575 JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
1576 "cancelled due to unsatisfied constraints");
Dianne Hackborn6d068262017-05-16 13:14:37 -07001577 }
1578 }
1579 }
1580
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001581 /**
1582 * Run through list of jobs and execute all possible - at least one is expired so we do
1583 * as many as we can.
1584 */
1585 private void queueReadyJobsForExecutionLocked() {
1586 if (DEBUG) {
1587 Slog.d(TAG, "queuing all ready jobs for execution:");
1588 }
1589 noteJobsNonpending(mPendingJobs);
1590 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001591 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001592 mJobs.forEachJob(mReadyQueueFunctor);
1593 mReadyQueueFunctor.postProcess();
Christopher Tate2f36fd62016-02-18 18:36:08 -08001594
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001595 if (DEBUG) {
1596 final int queuedJobs = mPendingJobs.size();
1597 if (queuedJobs == 0) {
1598 Slog.d(TAG, "No jobs pending.");
1599 } else {
1600 Slog.d(TAG, queuedJobs + " jobs queued.");
Christopher Tate2f36fd62016-02-18 18:36:08 -08001601 }
1602 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001603 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08001604
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001605 final class ReadyJobQueueFunctor implements JobStatusFunctor {
1606 ArrayList<JobStatus> newReadyJobs;
Christopher Tate2f36fd62016-02-18 18:36:08 -08001607
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001608 @Override
1609 public void process(JobStatus job) {
1610 if (isReadyToBeExecutedLocked(job)) {
Matthew Williams75fc5252014-09-02 16:17:53 -07001611 if (DEBUG) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001612 Slog.d(TAG, " queued " + job.toShortString());
Matthew Williams75fc5252014-09-02 16:17:53 -07001613 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001614 if (newReadyJobs == null) {
1615 newReadyJobs = new ArrayList<JobStatus>();
1616 }
1617 newReadyJobs.add(job);
Christopher Tate7060b042014-06-09 19:50:00 -07001618 }
1619 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001620
1621 public void postProcess() {
1622 if (newReadyJobs != null) {
1623 noteJobsPending(newReadyJobs);
1624 mPendingJobs.addAll(newReadyJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001625 if (mPendingJobs.size() > 1) {
1626 mPendingJobs.sort(mEnqueueTimeComparator);
1627 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001628 }
1629 newReadyJobs = null;
1630 }
1631 }
1632 private final ReadyJobQueueFunctor mReadyQueueFunctor = new ReadyJobQueueFunctor();
1633
1634 /**
1635 * The state of at least one job has changed. Here is where we could enforce various
1636 * policies on when we want to execute jobs.
1637 * Right now the policy is such:
1638 * If >1 of the ready jobs is idle mode we send all of them off
1639 * if more than 2 network connectivity jobs are ready we send them all off.
1640 * If more than 4 jobs total are ready we send them all off.
1641 * TODO: It would be nice to consolidate these sort of high-level policies somewhere.
1642 */
1643 final class MaybeReadyJobQueueFunctor implements JobStatusFunctor {
1644 int chargingCount;
1645 int batteryNotLowCount;
1646 int storageNotLowCount;
1647 int idleCount;
1648 int backoffCount;
1649 int connectivityCount;
1650 int contentCount;
1651 List<JobStatus> runnableJobs;
1652
1653 public MaybeReadyJobQueueFunctor() {
1654 reset();
1655 }
1656
1657 // Functor method invoked for each job via JobStore.forEachJob()
1658 @Override
1659 public void process(JobStatus job) {
1660 if (isReadyToBeExecutedLocked(job)) {
1661 try {
1662 if (ActivityManager.getService().isAppStartModeDisabled(job.getUid(),
1663 job.getJob().getService().getPackageName())) {
1664 Slog.w(TAG, "Aborting job " + job.getUid() + ":"
1665 + job.getJob().toString() + " -- package not allowed to start");
1666 mHandler.obtainMessage(MSG_STOP_JOB, job).sendToTarget();
1667 return;
1668 }
1669 } catch (RemoteException e) {
1670 }
1671 if (job.getNumFailures() > 0) {
1672 backoffCount++;
1673 }
1674 if (job.hasIdleConstraint()) {
1675 idleCount++;
1676 }
1677 if (job.hasConnectivityConstraint()) {
1678 connectivityCount++;
1679 }
1680 if (job.hasChargingConstraint()) {
1681 chargingCount++;
1682 }
1683 if (job.hasBatteryNotLowConstraint()) {
1684 batteryNotLowCount++;
1685 }
1686 if (job.hasStorageNotLowConstraint()) {
1687 storageNotLowCount++;
1688 }
1689 if (job.hasContentTriggerConstraint()) {
1690 contentCount++;
1691 }
1692 if (runnableJobs == null) {
1693 runnableJobs = new ArrayList<>();
1694 }
1695 runnableJobs.add(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001696 }
1697 }
1698
1699 public void postProcess() {
1700 if (backoffCount > 0 ||
1701 idleCount >= mConstants.MIN_IDLE_COUNT ||
1702 connectivityCount >= mConstants.MIN_CONNECTIVITY_COUNT ||
1703 chargingCount >= mConstants.MIN_CHARGING_COUNT ||
1704 batteryNotLowCount >= mConstants.MIN_BATTERY_NOT_LOW_COUNT ||
1705 storageNotLowCount >= mConstants.MIN_STORAGE_NOT_LOW_COUNT ||
1706 contentCount >= mConstants.MIN_CONTENT_COUNT ||
1707 (runnableJobs != null
1708 && runnableJobs.size() >= mConstants.MIN_READY_JOBS_COUNT)) {
1709 if (DEBUG) {
1710 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Running jobs.");
1711 }
1712 noteJobsPending(runnableJobs);
1713 mPendingJobs.addAll(runnableJobs);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07001714 if (mPendingJobs.size() > 1) {
1715 mPendingJobs.sort(mEnqueueTimeComparator);
1716 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001717 } else {
1718 if (DEBUG) {
1719 Slog.d(TAG, "maybeQueueReadyJobsForExecutionLocked: Not running anything.");
1720 }
1721 }
1722
1723 // Be ready for next time
1724 reset();
1725 }
1726
1727 private void reset() {
1728 chargingCount = 0;
1729 idleCount = 0;
1730 backoffCount = 0;
1731 connectivityCount = 0;
1732 batteryNotLowCount = 0;
1733 storageNotLowCount = 0;
1734 contentCount = 0;
1735 runnableJobs = null;
1736 }
1737 }
1738 private final MaybeReadyJobQueueFunctor mMaybeQueueFunctor = new MaybeReadyJobQueueFunctor();
1739
1740 private void maybeQueueReadyJobsForExecutionLocked() {
1741 if (DEBUG) Slog.d(TAG, "Maybe queuing ready jobs...");
1742
1743 noteJobsNonpending(mPendingJobs);
1744 mPendingJobs.clear();
Dianne Hackborn6d068262017-05-16 13:14:37 -07001745 stopNonReadyActiveJobsLocked();
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001746 mJobs.forEachJob(mMaybeQueueFunctor);
1747 mMaybeQueueFunctor.postProcess();
1748 }
1749
Christopher Tated1aebb32018-01-31 13:24:14 -08001750 /**
1751 * Heartbeat tracking. The heartbeat alarm is intentionally non-wakeup.
1752 */
1753 class HeartbeatAlarmListener implements AlarmManager.OnAlarmListener {
1754
1755 @Override
1756 public void onAlarm() {
1757 synchronized (mLock) {
1758 final long sinceLast = sElapsedRealtimeClock.millis() - mLastHeartbeatTime;
1759 final long beatsElapsed = sinceLast / mConstants.STANDBY_HEARTBEAT_TIME;
1760 if (beatsElapsed > 0) {
1761 mLastHeartbeatTime += beatsElapsed * mConstants.STANDBY_HEARTBEAT_TIME;
1762 advanceHeartbeatLocked(beatsElapsed);
1763 }
Christopher Tatea732f012017-10-26 17:26:53 -07001764 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001765 setNextHeartbeatAlarm();
Christopher Tatea732f012017-10-26 17:26:53 -07001766 }
Christopher Tatea732f012017-10-26 17:26:53 -07001767 }
1768
Christopher Tated1aebb32018-01-31 13:24:14 -08001769 // Intentionally does not touch the alarm timing
1770 void advanceHeartbeatLocked(long beatsElapsed) {
1771 mHeartbeat += beatsElapsed;
1772 if (DEBUG_STANDBY) {
1773 Slog.v(TAG, "Advancing standby heartbeat by " + beatsElapsed
1774 + " to " + mHeartbeat);
1775 }
1776 // Don't update ACTIVE or NEVER bucket milestones. Note that mHeartbeat
1777 // will be equal to mNextBucketHeartbeat[bucket] for one beat, during which
1778 // new jobs scheduled by apps in that bucket will be permitted to run
1779 // immediately.
1780 boolean didAdvanceBucket = false;
Christopher Tatea732f012017-10-26 17:26:53 -07001781 for (int i = 1; i < mNextBucketHeartbeat.length - 1; i++) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001782 // Did we reach or cross a bucket boundary?
1783 if (mHeartbeat >= mNextBucketHeartbeat[i]) {
1784 didAdvanceBucket = true;
1785 }
1786 while (mHeartbeat > mNextBucketHeartbeat[i]) {
Christopher Tatea732f012017-10-26 17:26:53 -07001787 mNextBucketHeartbeat[i] += mConstants.STANDBY_BEATS[i];
1788 }
1789 if (DEBUG_STANDBY) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001790 Slog.v(TAG, " Bucket " + i + " next heartbeat "
1791 + mNextBucketHeartbeat[i]);
Christopher Tatea732f012017-10-26 17:26:53 -07001792 }
1793 }
Christopher Tated1aebb32018-01-31 13:24:14 -08001794
1795 if (didAdvanceBucket) {
1796 if (DEBUG_STANDBY) {
1797 Slog.v(TAG, "Hit bucket boundary; reevaluating job runnability");
1798 }
1799 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
1800 }
1801 }
1802
1803 void setNextHeartbeatAlarm() {
1804 final long heartbeatLength;
1805 synchronized (mLock) {
1806 heartbeatLength = mConstants.STANDBY_HEARTBEAT_TIME;
1807 }
1808 final long now = sElapsedRealtimeClock.millis();
1809 final long nextBeatOrdinal = (now + heartbeatLength) / heartbeatLength;
1810 final long nextHeartbeat = nextBeatOrdinal * heartbeatLength;
1811 if (DEBUG_STANDBY) {
1812 Slog.i(TAG, "Setting heartbeat alarm for " + nextHeartbeat
1813 + " = " + TimeUtils.formatDuration(nextHeartbeat - now));
1814 }
1815 AlarmManager am = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
1816 am.setExact(AlarmManager.ELAPSED_REALTIME, nextHeartbeat,
1817 HEARTBEAT_TAG, mHeartbeatAlarm, mHandler);
Christopher Tatea732f012017-10-26 17:26:53 -07001818 }
1819
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001820 /**
1821 * Criteria for moving a job into the pending queue:
1822 * - It's ready.
1823 * - It's not pending.
1824 * - It's not already running on a JSC.
1825 * - The user that requested the job is running.
Christopher Tatea732f012017-10-26 17:26:53 -07001826 * - The job's standby bucket has come due to be runnable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001827 * - The component is enabled and runnable.
1828 */
1829 private boolean isReadyToBeExecutedLocked(JobStatus job) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001830 final boolean jobReady = job.isReady();
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001831
1832 if (DEBUG) {
1833 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1834 + " ready=" + jobReady);
1835 }
1836
1837 // This is a condition that is very likely to be false (most jobs that are
1838 // scheduled are sitting there, not ready yet) and very cheap to check (just
1839 // a few conditions on data in JobStatus).
1840 if (!jobReady) {
1841 return false;
1842 }
1843
1844 final boolean jobExists = mJobs.containsJob(job);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001845
1846 final int userId = job.getUserId();
1847 final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
1848
1849 if (DEBUG) {
1850 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001851 + " exists=" + jobExists + " userStarted=" + userStarted);
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001852 }
1853
Dianne Hackborn28d1b662017-04-21 14:17:23 -07001854 // These are also fairly cheap to check, though they typically will not
1855 // be conditions we fail.
1856 if (!jobExists || !userStarted) {
1857 return false;
1858 }
1859
1860 final boolean jobPending = mPendingJobs.contains(job);
1861 final boolean jobActive = isCurrentlyActiveLocked(job);
1862
1863 if (DEBUG) {
1864 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1865 + " pending=" + jobPending + " active=" + jobActive);
1866 }
1867
1868 // These can be a little more expensive (especially jobActive, since we need to
1869 // go through the array of all potentially active jobs), so we are doing them
1870 // later... but still before checking with the package manager!
1871 if (jobPending || jobActive) {
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001872 return false;
1873 }
1874
Christopher Tatea732f012017-10-26 17:26:53 -07001875 // If the app is in a non-active standby bucket, make sure we've waited
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001876 // an appropriate amount of time since the last invocation. During device-
1877 // wide parole, standby bucketing is ignored.
Makoto Onuki959acb52018-01-26 14:10:03 -08001878 //
1879 // But if a job has FLAG_EXEMPT_FROM_APP_STANDBY, don't check it.
1880 if (!mInParole && !job.getJob().isExemptedFromAppStandby()) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001881 final int bucket = job.getStandbyBucket();
1882 if (mHeartbeat < mNextBucketHeartbeat[bucket]) {
Christopher Tated1aebb32018-01-31 13:24:14 -08001883 // Only skip this job if the app is still waiting for the end of its nominal
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001884 // bucket interval. Once it's waited that long, we let it go ahead and clear.
1885 // The final (NEVER) bucket is special; we never age those apps' jobs into
1886 // runnability.
Christopher Tated1aebb32018-01-31 13:24:14 -08001887 final long appLastRan = heartbeatWhenJobsLastRun(job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001888 if (bucket >= mConstants.STANDBY_BEATS.length
Christopher Tated1aebb32018-01-31 13:24:14 -08001889 || (mHeartbeat > appLastRan
1890 && mHeartbeat < appLastRan + mConstants.STANDBY_BEATS[bucket])) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001891 // TODO: log/trace that we're deferring the job due to bucketing if we hit this
1892 if (job.getWhenStandbyDeferred() == 0) {
1893 if (DEBUG_STANDBY) {
1894 Slog.v(TAG, "Bucket deferral: " + mHeartbeat + " < "
Christopher Tated1aebb32018-01-31 13:24:14 -08001895 + (appLastRan + mConstants.STANDBY_BEATS[bucket])
1896 + " for " + job);
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001897 }
1898 job.setWhenStandbyDeferred(sElapsedRealtimeClock.millis());
Christopher Tate0c4d7682017-12-06 15:10:22 -08001899 }
Christopher Tatea5a85bd2018-01-03 17:20:36 -08001900 return false;
1901 } else {
1902 if (DEBUG_STANDBY) {
1903 Slog.v(TAG, "Bucket deferred job aged into runnability at "
1904 + mHeartbeat + " : " + job);
1905 }
Christopher Tate0c4d7682017-12-06 15:10:22 -08001906 }
Christopher Tatea732f012017-10-26 17:26:53 -07001907 }
Christopher Tatea732f012017-10-26 17:26:53 -07001908 }
1909
1910 // The expensive check last: validate that the defined package+service is
1911 // still present & viable.
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001912 final boolean componentPresent;
1913 try {
1914 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
1915 job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
1916 userId) != null);
1917 } catch (RemoteException e) {
1918 throw e.rethrowAsRuntimeException();
1919 }
1920
1921 if (DEBUG) {
1922 Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
1923 + " componentPresent=" + componentPresent);
1924 }
1925
1926 // Everything else checked out so far, so this is the final yes/no check
1927 return componentPresent;
1928 }
1929
1930 /**
Dianne Hackbornf9bac162017-04-20 17:17:48 -07001931 * Reconcile jobs in the pending queue against available execution contexts.
1932 * A controller can force a job into the pending queue even if it's already running, but
1933 * here is where we decide whether to actually execute it.
1934 */
1935 private void maybeRunPendingJobsLocked() {
1936 if (DEBUG) {
1937 Slog.d(TAG, "pending queue: " + mPendingJobs.size() + " jobs.");
1938 }
1939 assignJobsToContextsLocked();
1940 reportActiveLocked();
Christopher Tate7060b042014-06-09 19:50:00 -07001941 }
1942
Dianne Hackborn807de782016-04-07 17:54:41 -07001943 private int adjustJobPriority(int curPriority, JobStatus job) {
1944 if (curPriority < JobInfo.PRIORITY_TOP_APP) {
1945 float factor = mJobPackageTracker.getLoadFactor(job);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001946 if (factor >= mConstants.HEAVY_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001947 curPriority += JobInfo.PRIORITY_ADJ_ALWAYS_RUNNING;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001948 } else if (factor >= mConstants.MODERATE_USE_FACTOR) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001949 curPriority += JobInfo.PRIORITY_ADJ_OFTEN_RUNNING;
1950 }
1951 }
1952 return curPriority;
1953 }
1954
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001955 private int evaluateJobPriorityLocked(JobStatus job) {
1956 int priority = job.getPriority();
1957 if (priority >= JobInfo.PRIORITY_FOREGROUND_APP) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001958 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001959 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08001960 int override = mUidPriorityOverride.get(job.getSourceUid(), 0);
1961 if (override != 0) {
Dianne Hackborn807de782016-04-07 17:54:41 -07001962 return adjustJobPriority(override, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001963 }
Dianne Hackborn807de782016-04-07 17:54:41 -07001964 return adjustJobPriority(priority, job);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08001965 }
1966
Christopher Tate7060b042014-06-09 19:50:00 -07001967 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +00001968 * Takes jobs from pending queue and runs them on available contexts.
1969 * If no contexts are available, preempts lower priority jobs to
1970 * run higher priority ones.
1971 * Lock on mJobs before calling this function.
1972 */
Dianne Hackbornb0001f62016-02-16 10:30:33 -08001973 private void assignJobsToContextsLocked() {
Shreyas Basarge5db09082016-01-07 13:38:29 +00001974 if (DEBUG) {
1975 Slog.d(TAG, printPendingQueue());
1976 }
1977
Dianne Hackborn970510b2016-02-24 16:56:42 -08001978 int memLevel;
1979 try {
Sudheer Shankadc589ac2016-11-10 15:30:17 -08001980 memLevel = ActivityManager.getService().getMemoryTrimLevel();
Dianne Hackborn970510b2016-02-24 16:56:42 -08001981 } catch (RemoteException e) {
1982 memLevel = ProcessStats.ADJ_MEM_FACTOR_NORMAL;
1983 }
1984 switch (memLevel) {
1985 case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001986 mMaxActiveJobs = mConstants.BG_MODERATE_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001987 break;
1988 case ProcessStats.ADJ_MEM_FACTOR_LOW:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001989 mMaxActiveJobs = mConstants.BG_LOW_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001990 break;
1991 case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001992 mMaxActiveJobs = mConstants.BG_CRITICAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001993 break;
1994 default:
Dianne Hackborne9a988c2016-05-27 17:59:40 -07001995 mMaxActiveJobs = mConstants.BG_NORMAL_JOB_COUNT;
Dianne Hackborn970510b2016-02-24 16:56:42 -08001996 break;
1997 }
1998
1999 JobStatus[] contextIdToJobMap = mTmpAssignContextIdToJobMap;
2000 boolean[] act = mTmpAssignAct;
2001 int[] preferredUidForContext = mTmpAssignPreferredUidForContext;
2002 int numActive = 0;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002003 int numForeground = 0;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002004 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
2005 final JobServiceContext js = mActiveServices.get(i);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002006 final JobStatus status = js.getRunningJobLocked();
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002007 if ((contextIdToJobMap[i] = status) != null) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08002008 numActive++;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002009 if (status.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP) {
2010 numForeground++;
2011 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002012 }
2013 act[i] = false;
2014 preferredUidForContext[i] = js.getPreferredUid();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002015 }
2016 if (DEBUG) {
2017 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
2018 }
Dianne Hackborn970510b2016-02-24 16:56:42 -08002019 for (int i=0; i<mPendingJobs.size(); i++) {
2020 JobStatus nextPending = mPendingJobs.get(i);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002021
2022 // If job is already running, go to next job.
2023 int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
2024 if (jobRunningContext != -1) {
2025 continue;
2026 }
2027
Dianne Hackborn970510b2016-02-24 16:56:42 -08002028 final int priority = evaluateJobPriorityLocked(nextPending);
2029 nextPending.lastEvaluatedPriority = priority;
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002030
Shreyas Basarge5db09082016-01-07 13:38:29 +00002031 // Find a context for nextPending. The context should be available OR
2032 // it should have lowest priority among all running jobs
2033 // (sharing the same Uid as nextPending)
2034 int minPriority = Integer.MAX_VALUE;
2035 int minPriorityContextId = -1;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002036 for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
2037 JobStatus job = contextIdToJobMap[j];
2038 int preferredUid = preferredUidForContext[j];
Shreyas Basarge347c2782016-01-15 18:24:36 +00002039 if (job == null) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002040 if ((numActive < mMaxActiveJobs ||
2041 (priority >= JobInfo.PRIORITY_TOP_APP &&
2042 numForeground < mConstants.FG_JOB_COUNT)) &&
Dianne Hackborn970510b2016-02-24 16:56:42 -08002043 (preferredUid == nextPending.getUid() ||
2044 preferredUid == JobServiceContext.NO_PREFERRED_UID)) {
2045 // This slot is free, and we haven't yet hit the limit on
2046 // concurrent jobs... we can just throw the job in to here.
2047 minPriorityContextId = j;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002048 break;
2049 }
Shreyas Basarge347c2782016-01-15 18:24:36 +00002050 // No job on this context, but nextPending can't run here because
Dianne Hackborn970510b2016-02-24 16:56:42 -08002051 // the context has a preferred Uid or we have reached the limit on
2052 // concurrent jobs.
Shreyas Basarge347c2782016-01-15 18:24:36 +00002053 continue;
2054 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002055 if (job.getUid() != nextPending.getUid()) {
2056 continue;
2057 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002058 if (evaluateJobPriorityLocked(job) >= nextPending.lastEvaluatedPriority) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002059 continue;
2060 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002061 if (minPriority > nextPending.lastEvaluatedPriority) {
2062 minPriority = nextPending.lastEvaluatedPriority;
Dianne Hackborn970510b2016-02-24 16:56:42 -08002063 minPriorityContextId = j;
Shreyas Basarge5db09082016-01-07 13:38:29 +00002064 }
2065 }
2066 if (minPriorityContextId != -1) {
2067 contextIdToJobMap[minPriorityContextId] = nextPending;
2068 act[minPriorityContextId] = true;
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002069 numActive++;
2070 if (priority >= JobInfo.PRIORITY_TOP_APP) {
2071 numForeground++;
2072 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002073 }
2074 }
2075 if (DEBUG) {
2076 Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
2077 }
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002078 mJobPackageTracker.noteConcurrency(numActive, numForeground);
Dianne Hackborn970510b2016-02-24 16:56:42 -08002079 for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
Shreyas Basarge5db09082016-01-07 13:38:29 +00002080 boolean preservePreferredUid = false;
2081 if (act[i]) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002082 JobStatus js = mActiveServices.get(i).getRunningJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002083 if (js != null) {
2084 if (DEBUG) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -07002085 Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJobLocked());
Shreyas Basarge5db09082016-01-07 13:38:29 +00002086 }
2087 // preferredUid will be set to uid of currently running job.
Dianne Hackborn342e6032017-04-13 18:04:31 -07002088 mActiveServices.get(i).preemptExecutingJobLocked();
Shreyas Basarge5db09082016-01-07 13:38:29 +00002089 preservePreferredUid = true;
2090 } else {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002091 final JobStatus pendingJob = contextIdToJobMap[i];
Shreyas Basarge5db09082016-01-07 13:38:29 +00002092 if (DEBUG) {
2093 Slog.d(TAG, "About to run job on context "
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002094 + String.valueOf(i) + ", job: " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002095 }
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002096 for (int ic=0; ic<mControllers.size(); ic++) {
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002097 mControllers.get(ic).prepareForExecutionLocked(pendingJob);
Dianne Hackborn1a30bd92016-01-11 11:05:00 -08002098 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002099 if (!mActiveServices.get(i).executeRunnableJob(pendingJob)) {
2100 Slog.d(TAG, "Error executing " + pendingJob);
Shreyas Basarge5db09082016-01-07 13:38:29 +00002101 }
Dianne Hackborn807de782016-04-07 17:54:41 -07002102 if (mPendingJobs.remove(pendingJob)) {
2103 mJobPackageTracker.noteNonpending(pendingJob);
2104 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002105 }
2106 }
2107 if (!preservePreferredUid) {
2108 mActiveServices.get(i).clearPreferredUid();
2109 }
2110 }
2111 }
2112
2113 int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
2114 for (int i=0; i<map.length; i++) {
2115 if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
2116 return i;
2117 }
2118 }
2119 return -1;
2120 }
2121
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002122 final class LocalService implements JobSchedulerInternal {
2123
2124 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002125 * The current bucket heartbeat ordinal
2126 */
2127 public long currentHeartbeat() {
2128 return getCurrentHeartbeat();
2129 }
2130
2131 /**
2132 * Heartbeat ordinal at which the given standby bucket's jobs next become runnable
2133 */
2134 public long nextHeartbeatForBucket(int bucket) {
2135 synchronized (mLock) {
2136 return mNextBucketHeartbeat[bucket];
2137 }
2138 }
2139
2140 /**
Christopher Tate435c2f42018-01-18 12:59:15 -08002141 * Heartbeat ordinal for the given app. This is typically the heartbeat at which
2142 * the app last ran jobs, so that a newly-scheduled job in an app that hasn't run
2143 * jobs in a long time is immediately runnable even if the app is bucketed into
2144 * an infrequent time allocation.
2145 */
2146 public long baseHeartbeatForApp(String packageName, @UserIdInt int userId,
2147 final int appStandbyBucket) {
2148 if (appStandbyBucket == 0 ||
2149 appStandbyBucket >= mConstants.STANDBY_BEATS.length) {
2150 // ACTIVE => everything can be run right away
2151 // NEVER => we won't run them anyway, so let them go in the future
2152 // as soon as the app enters normal use
Christopher Tated1aebb32018-01-31 13:24:14 -08002153 if (DEBUG_STANDBY) {
2154 Slog.v(TAG, "Base heartbeat forced ZERO for new job in "
2155 + packageName + "/" + userId);
2156 }
Christopher Tate435c2f42018-01-18 12:59:15 -08002157 return 0;
2158 }
2159
Christopher Tated1aebb32018-01-31 13:24:14 -08002160 final long baseHeartbeat = heartbeatWhenJobsLastRun(packageName, userId);
2161 if (DEBUG_STANDBY) {
2162 Slog.v(TAG, "Base heartbeat " + baseHeartbeat + " for new job in "
2163 + packageName + "/" + userId);
2164 }
2165 return baseHeartbeat;
Christopher Tate435c2f42018-01-18 12:59:15 -08002166 }
2167
2168 /**
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002169 * Returns a list of all pending jobs. A running job is not considered pending. Periodic
2170 * jobs are always considered pending.
2171 */
Amith Yamasanicb926fc2016-03-14 17:15:20 -07002172 @Override
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002173 public List<JobInfo> getSystemScheduledPendingJobs() {
2174 synchronized (mLock) {
2175 final List<JobInfo> pendingJobs = new ArrayList<JobInfo>();
2176 mJobs.forEachJob(Process.SYSTEM_UID, new JobStatusFunctor() {
2177 @Override
2178 public void process(JobStatus job) {
2179 if (job.getJob().isPeriodic() || !isCurrentlyActiveLocked(job)) {
2180 pendingJobs.add(job.getJob());
2181 }
2182 }
2183 });
2184 return pendingJobs;
2185 }
2186 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002187
2188 @Override
Christopher Tate1d99c392017-12-07 16:54:04 -08002189 public void cancelJobsForUid(int uid, String reason) {
2190 JobSchedulerService.this.cancelJobsForUid(uid, reason);
2191 }
2192
2193 @Override
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002194 public void addBackingUpUid(int uid) {
2195 synchronized (mLock) {
2196 // No need to actually do anything here, since for a full backup the
2197 // activity manager will kill the process which will kill the job (and
2198 // cause it to restart, but now it can't run).
2199 mBackingUpUids.put(uid, uid);
2200 }
2201 }
2202
2203 @Override
2204 public void removeBackingUpUid(int uid) {
2205 synchronized (mLock) {
2206 mBackingUpUids.delete(uid);
2207 // If there are any jobs for this uid, we need to rebuild the pending list
2208 // in case they are now ready to run.
2209 if (mJobs.countJobsForUid(uid) > 0) {
2210 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2211 }
2212 }
2213 }
2214
2215 @Override
2216 public void clearAllBackingUpUids() {
2217 synchronized (mLock) {
2218 if (mBackingUpUids.size() > 0) {
2219 mBackingUpUids.clear();
2220 mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
2221 }
2222 }
2223 }
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002224
2225 @Override
Christopher Tated117b292018-01-05 17:32:36 -08002226 public void reportAppUsage(String packageName, int userId) {
2227 JobSchedulerService.this.reportAppUsage(packageName, userId);
2228 }
2229
2230 @Override
Makoto Onukie7b02982017-08-24 14:23:36 -07002231 public JobStorePersistStats getPersistStats() {
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002232 synchronized (mLock) {
Makoto Onukie7b02982017-08-24 14:23:36 -07002233 return new JobStorePersistStats(mJobs.getPersistStats());
Makoto Onukidd4b14f2017-08-17 14:03:48 -07002234 }
2235 }
Shreyas Basargecbf5ae92016-03-08 16:13:06 +00002236 }
2237
Shreyas Basarge5db09082016-01-07 13:38:29 +00002238 /**
Christopher Tatea732f012017-10-26 17:26:53 -07002239 * Tracking of app assignments to standby buckets
2240 */
2241 final class StandbyTracker extends AppIdleStateChangeListener {
Christopher Tate435c2f42018-01-18 12:59:15 -08002242
Christopher Tatea732f012017-10-26 17:26:53 -07002243 // AppIdleStateChangeListener interface for live updates
2244
2245 @Override
Christopher Tate435c2f42018-01-18 12:59:15 -08002246 public void onAppIdleStateChanged(final String packageName, final @UserIdInt int userId,
Christopher Tatea732f012017-10-26 17:26:53 -07002247 boolean idle, int bucket) {
2248 final int uid = mLocalPM.getPackageUid(packageName,
2249 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2250 if (uid < 0) {
2251 if (DEBUG_STANDBY) {
2252 Slog.i(TAG, "App idle state change for unknown app "
2253 + packageName + "/" + userId);
2254 }
2255 return;
2256 }
2257
2258 final int bucketIndex = standbyBucketToBucketIndex(bucket);
2259 // update job bookkeeping out of band
2260 BackgroundThread.getHandler().post(() -> {
2261 if (DEBUG_STANDBY) {
2262 Slog.i(TAG, "Moving uid " + uid + " to bucketIndex " + bucketIndex);
2263 }
2264 synchronized (mLock) {
Christopher Tate93defea2018-02-20 15:57:17 -08002265 mJobs.forEachJobForSourceUid(uid, job -> {
2266 // double-check uid vs package name to disambiguate shared uids
2267 if (packageName.equals(job.getSourcePackageName())) {
2268 job.setStandbyBucket(bucketIndex);
2269 }
2270 });
Christopher Tatea732f012017-10-26 17:26:53 -07002271 onControllerStateChanged();
2272 }
2273 });
2274 }
2275
2276 @Override
2277 public void onParoleStateChanged(boolean isParoleOn) {
Christopher Tatea5a85bd2018-01-03 17:20:36 -08002278 if (DEBUG_STANDBY) {
2279 Slog.i(TAG, "Global parole state now " + (isParoleOn ? "ON" : "OFF"));
2280 }
2281 mInParole = isParoleOn;
Christopher Tatea732f012017-10-26 17:26:53 -07002282 }
Christopher Tated117b292018-01-05 17:32:36 -08002283
2284 @Override
2285 public void onUserInteractionStarted(String packageName, int userId) {
2286 final int uid = mLocalPM.getPackageUid(packageName,
2287 PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
2288 if (uid < 0) {
2289 // Quietly ignore; the case is already logged elsewhere
2290 return;
2291 }
2292
Amith Yamasani977e11f2018-02-16 11:29:54 -08002293 long sinceLast = mUsageStats.getTimeSinceLastJobRun(packageName, userId);
2294 if (sinceLast > 2 * DateUtils.DAY_IN_MILLIS) {
2295 // Too long ago, not worth logging
2296 sinceLast = 0L;
2297 }
Christopher Tated117b292018-01-05 17:32:36 -08002298 final DeferredJobCounter counter = new DeferredJobCounter();
2299 synchronized (mLock) {
2300 mJobs.forEachJobForSourceUid(uid, counter);
2301 }
Amith Yamasani977e11f2018-02-16 11:29:54 -08002302 if (counter.numDeferred() > 0 || sinceLast > 0) {
2303 BatteryStatsInternal mBatteryStatsInternal = LocalServices.getService
2304 (BatteryStatsInternal.class);
2305 mBatteryStatsInternal.noteJobsDeferred(uid, counter.numDeferred(), sinceLast);
2306 }
Christopher Tated117b292018-01-05 17:32:36 -08002307 }
2308 }
2309
2310 static class DeferredJobCounter implements JobStatusFunctor {
2311 private int mDeferred = 0;
2312
2313 public int numDeferred() {
2314 return mDeferred;
2315 }
2316
2317 @Override
2318 public void process(JobStatus job) {
2319 if (job.getWhenStandbyDeferred() > 0) {
2320 mDeferred++;
2321 }
2322 }
Christopher Tatea732f012017-10-26 17:26:53 -07002323 }
2324
2325 public static int standbyBucketToBucketIndex(int bucket) {
2326 // Normalize AppStandby constants to indices into our bookkeeping
Amith Yamasaniafbccb72017-11-27 10:44:24 -08002327 if (bucket == UsageStatsManager.STANDBY_BUCKET_NEVER) return 4;
2328 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_RARE) return 3;
2329 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_FREQUENT) return 2;
2330 else if (bucket >= UsageStatsManager.STANDBY_BUCKET_WORKING_SET) return 1;
Christopher Tatea732f012017-10-26 17:26:53 -07002331 else return 0;
2332 }
2333
Christopher Tated1aebb32018-01-31 13:24:14 -08002334 // Static to support external callers
Christopher Tatea732f012017-10-26 17:26:53 -07002335 public static int standbyBucketForPackage(String packageName, int userId, long elapsedNow) {
2336 UsageStatsManagerInternal usageStats = LocalServices.getService(
2337 UsageStatsManagerInternal.class);
2338 int bucket = usageStats != null
2339 ? usageStats.getAppStandbyBucket(packageName, userId, elapsedNow)
2340 : 0;
2341
2342 bucket = standbyBucketToBucketIndex(bucket);
2343
2344 if (DEBUG_STANDBY) {
2345 Slog.v(TAG, packageName + "/" + userId + " standby bucket index: " + bucket);
2346 }
2347 return bucket;
2348 }
2349
2350 /**
Christopher Tate7060b042014-06-09 19:50:00 -07002351 * Binder stub trampoline implementation
2352 */
2353 final class JobSchedulerStub extends IJobScheduler.Stub {
2354 /** Cache determination of whether a given app can persist jobs
2355 * key is uid of the calling app; value is undetermined/true/false
2356 */
2357 private final SparseArray<Boolean> mPersistCache = new SparseArray<Boolean>();
2358
2359 // Enforce that only the app itself (or shared uid participant) can schedule a
2360 // job that runs one of the app's services, as well as verifying that the
2361 // named service properly requires the BIND_JOB_SERVICE permission
2362 private void enforceValidJobRequest(int uid, JobInfo job) {
Christopher Tate5568f542014-06-18 13:53:31 -07002363 final IPackageManager pm = AppGlobals.getPackageManager();
Christopher Tate7060b042014-06-09 19:50:00 -07002364 final ComponentName service = job.getService();
2365 try {
Jeff Sharkeyc7bacab2016-02-09 15:56:11 -07002366 ServiceInfo si = pm.getServiceInfo(service,
Jeff Sharkey8a372a02016-03-16 16:25:45 -06002367 PackageManager.MATCH_DIRECT_BOOT_AWARE
2368 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
Jeff Sharkey12c0da42016-02-25 17:10:50 -07002369 UserHandle.getUserId(uid));
Christopher Tate5568f542014-06-18 13:53:31 -07002370 if (si == null) {
2371 throw new IllegalArgumentException("No such service " + service);
2372 }
Christopher Tate7060b042014-06-09 19:50:00 -07002373 if (si.applicationInfo.uid != uid) {
2374 throw new IllegalArgumentException("uid " + uid +
2375 " cannot schedule job in " + service.getPackageName());
2376 }
2377 if (!JobService.PERMISSION_BIND.equals(si.permission)) {
2378 throw new IllegalArgumentException("Scheduled service " + service
2379 + " does not require android.permission.BIND_JOB_SERVICE permission");
2380 }
Christopher Tate5568f542014-06-18 13:53:31 -07002381 } catch (RemoteException e) {
2382 // Can't happen; the Package Manager is in this same process
Christopher Tate7060b042014-06-09 19:50:00 -07002383 }
2384 }
2385
2386 private boolean canPersistJobs(int pid, int uid) {
2387 // If we get this far we're good to go; all we need to do now is check
2388 // whether the app is allowed to persist its scheduled work.
2389 final boolean canPersist;
2390 synchronized (mPersistCache) {
2391 Boolean cached = mPersistCache.get(uid);
2392 if (cached != null) {
2393 canPersist = cached.booleanValue();
2394 } else {
2395 // Persisting jobs is tantamount to running at boot, so we permit
2396 // it when the app has declared that it uses the RECEIVE_BOOT_COMPLETED
2397 // permission
2398 int result = getContext().checkPermission(
2399 android.Manifest.permission.RECEIVE_BOOT_COMPLETED, pid, uid);
2400 canPersist = (result == PackageManager.PERMISSION_GRANTED);
2401 mPersistCache.put(uid, canPersist);
2402 }
2403 }
2404 return canPersist;
2405 }
2406
Makoto Onuki959acb52018-01-26 14:10:03 -08002407 private void validateJobFlags(JobInfo job, int callingUid) {
2408 if ((job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0) {
2409 getContext().enforceCallingOrSelfPermission(
2410 android.Manifest.permission.CONNECTIVITY_INTERNAL, TAG);
2411 }
2412 if ((job.getFlags() & JobInfo.FLAG_EXEMPT_FROM_APP_STANDBY) != 0) {
2413 if (callingUid != Process.SYSTEM_UID) {
2414 throw new SecurityException("Job has invalid flags");
2415 }
Makoto Onuki2b5811a2018-02-08 11:09:42 -08002416 if (job.isPeriodic()) {
2417 Slog.wtf(TAG, "Periodic jobs mustn't have"
2418 + " FLAG_EXEMPT_FROM_APP_STANDBY. Job=" + job);
Makoto Onuki959acb52018-01-26 14:10:03 -08002419 }
2420 }
2421 }
2422
Christopher Tate7060b042014-06-09 19:50:00 -07002423 // IJobScheduler implementation
2424 @Override
2425 public int schedule(JobInfo job) throws RemoteException {
2426 if (DEBUG) {
Matthew Williamsee410da2014-07-25 11:30:40 -07002427 Slog.d(TAG, "Scheduling job: " + job.toString());
Christopher Tate7060b042014-06-09 19:50:00 -07002428 }
2429 final int pid = Binder.getCallingPid();
2430 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002431 final int userId = UserHandle.getUserId(uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002432
2433 enforceValidJobRequest(uid, job);
Matthew Williams900c67f2014-07-09 12:46:53 -07002434 if (job.isPersisted()) {
2435 if (!canPersistJobs(pid, uid)) {
2436 throw new IllegalArgumentException("Error: requested job be persisted without"
2437 + " holding RECEIVE_BOOT_COMPLETED permission.");
2438 }
2439 }
Christopher Tate7060b042014-06-09 19:50:00 -07002440
Makoto Onuki959acb52018-01-26 14:10:03 -08002441 validateJobFlags(job, uid);
Jeff Sharkey785f4942016-07-14 10:31:15 -06002442
Christopher Tate7060b042014-06-09 19:50:00 -07002443 long ident = Binder.clearCallingIdentity();
2444 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002445 return JobSchedulerService.this.scheduleAsPackage(job, null, uid, null, userId,
2446 null);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002447 } finally {
2448 Binder.restoreCallingIdentity(ident);
2449 }
2450 }
2451
2452 // IJobScheduler implementation
2453 @Override
2454 public int enqueue(JobInfo job, JobWorkItem work) throws RemoteException {
2455 if (DEBUG) {
2456 Slog.d(TAG, "Enqueueing job: " + job.toString() + " work: " + work);
2457 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002458 final int uid = Binder.getCallingUid();
Christopher Tatea732f012017-10-26 17:26:53 -07002459 final int userId = UserHandle.getUserId(uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002460
2461 enforceValidJobRequest(uid, job);
2462 if (job.isPersisted()) {
2463 throw new IllegalArgumentException("Can't enqueue work for persisted jobs");
2464 }
2465 if (work == null) {
2466 throw new NullPointerException("work is null");
2467 }
2468
Makoto Onuki959acb52018-01-26 14:10:03 -08002469 validateJobFlags(job, uid);
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002470
2471 long ident = Binder.clearCallingIdentity();
2472 try {
Christopher Tatea732f012017-10-26 17:26:53 -07002473 return JobSchedulerService.this.scheduleAsPackage(job, work, uid, null, userId,
2474 null);
Christopher Tate7060b042014-06-09 19:50:00 -07002475 } finally {
2476 Binder.restoreCallingIdentity(ident);
2477 }
2478 }
2479
2480 @Override
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002481 public int scheduleAsPackage(JobInfo job, String packageName, int userId, String tag)
Shreyas Basarge968ac752016-01-11 23:09:26 +00002482 throws RemoteException {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002483 final int callerUid = Binder.getCallingUid();
Shreyas Basarge968ac752016-01-11 23:09:26 +00002484 if (DEBUG) {
Christopher Tate2f36fd62016-02-18 18:36:08 -08002485 Slog.d(TAG, "Caller uid " + callerUid + " scheduling job: " + job.toString()
Christopher Tatea732f012017-10-26 17:26:53 -07002486 + " on behalf of " + packageName + "/");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002487 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002488
2489 if (packageName == null) {
2490 throw new NullPointerException("Must specify a package for scheduleAsPackage()");
Shreyas Basarge968ac752016-01-11 23:09:26 +00002491 }
Christopher Tate2f36fd62016-02-18 18:36:08 -08002492
2493 int mayScheduleForOthers = getContext().checkCallingOrSelfPermission(
2494 android.Manifest.permission.UPDATE_DEVICE_STATS);
2495 if (mayScheduleForOthers != PackageManager.PERMISSION_GRANTED) {
2496 throw new SecurityException("Caller uid " + callerUid
2497 + " not permitted to schedule jobs for other apps");
2498 }
2499
Makoto Onuki959acb52018-01-26 14:10:03 -08002500 validateJobFlags(job, callerUid);
Jeff Sharkey4f100402016-05-03 17:44:23 -06002501
Shreyas Basarge968ac752016-01-11 23:09:26 +00002502 long ident = Binder.clearCallingIdentity();
2503 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -07002504 return JobSchedulerService.this.scheduleAsPackage(job, null, callerUid,
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002505 packageName, userId, tag);
Shreyas Basarge968ac752016-01-11 23:09:26 +00002506 } finally {
2507 Binder.restoreCallingIdentity(ident);
2508 }
2509 }
2510
2511 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002512 public List<JobInfo> getAllPendingJobs() throws RemoteException {
2513 final int uid = Binder.getCallingUid();
2514
2515 long ident = Binder.clearCallingIdentity();
2516 try {
2517 return JobSchedulerService.this.getPendingJobs(uid);
2518 } finally {
2519 Binder.restoreCallingIdentity(ident);
2520 }
2521 }
2522
2523 @Override
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002524 public JobInfo getPendingJob(int jobId) throws RemoteException {
2525 final int uid = Binder.getCallingUid();
2526
2527 long ident = Binder.clearCallingIdentity();
2528 try {
2529 return JobSchedulerService.this.getPendingJob(uid, jobId);
2530 } finally {
2531 Binder.restoreCallingIdentity(ident);
2532 }
2533 }
2534
2535 @Override
Christopher Tate7060b042014-06-09 19:50:00 -07002536 public void cancelAll() throws RemoteException {
2537 final int uid = Binder.getCallingUid();
Christopher Tate7060b042014-06-09 19:50:00 -07002538 long ident = Binder.clearCallingIdentity();
2539 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002540 JobSchedulerService.this.cancelJobsForUid(uid,
2541 "cancelAll() called by app, callingUid=" + uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002542 } finally {
2543 Binder.restoreCallingIdentity(ident);
2544 }
2545 }
2546
2547 @Override
2548 public void cancel(int jobId) throws RemoteException {
2549 final int uid = Binder.getCallingUid();
2550
2551 long ident = Binder.clearCallingIdentity();
2552 try {
Makoto Onukid2bfec62018-01-12 13:58:01 -08002553 JobSchedulerService.this.cancelJob(uid, jobId, uid);
Christopher Tate7060b042014-06-09 19:50:00 -07002554 } finally {
2555 Binder.restoreCallingIdentity(ident);
2556 }
2557 }
2558
2559 /**
2560 * "dumpsys" infrastructure
2561 */
2562 @Override
2563 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkey6df866a2017-03-31 14:08:23 -06002564 if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
Christopher Tate7060b042014-06-09 19:50:00 -07002565
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002566 int filterUid = -1;
2567 boolean proto = false;
2568 if (!ArrayUtils.isEmpty(args)) {
2569 int opti = 0;
2570 while (opti < args.length) {
2571 String arg = args[opti];
2572 if ("-h".equals(arg)) {
2573 dumpHelp(pw);
2574 return;
2575 } else if ("-a".equals(arg)) {
2576 // Ignore, we always dump all.
2577 } else if ("--proto".equals(arg)) {
2578 proto = true;
2579 } else if (arg.length() > 0 && arg.charAt(0) == '-') {
2580 pw.println("Unknown option: " + arg);
2581 return;
2582 } else {
2583 break;
2584 }
2585 opti++;
2586 }
2587 if (opti < args.length) {
2588 String pkg = args[opti];
2589 try {
2590 filterUid = getContext().getPackageManager().getPackageUid(pkg,
2591 PackageManager.MATCH_ANY_USER);
2592 } catch (NameNotFoundException ignored) {
2593 pw.println("Invalid package: " + pkg);
2594 return;
2595 }
2596 }
2597 }
2598
Christopher Tate7060b042014-06-09 19:50:00 -07002599 long identityToken = Binder.clearCallingIdentity();
2600 try {
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002601 if (proto) {
2602 JobSchedulerService.this.dumpInternalProto(fd, filterUid);
2603 } else {
2604 JobSchedulerService.this.dumpInternal(pw, filterUid);
2605 }
Christopher Tate7060b042014-06-09 19:50:00 -07002606 } finally {
2607 Binder.restoreCallingIdentity(identityToken);
2608 }
2609 }
Christopher Tate5d346052016-03-08 12:56:08 -08002610
2611 @Override
2612 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
Dianne Hackborn354736e2016-08-22 17:00:05 -07002613 String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
Christopher Tate5d346052016-03-08 12:56:08 -08002614 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
Dianne Hackborn354736e2016-08-22 17:00:05 -07002615 this, in, out, err, args, callback, resultReceiver);
Christopher Tate5d346052016-03-08 12:56:08 -08002616 }
Shreyas Basarge5db09082016-01-07 13:38:29 +00002617 };
2618
Christopher Tate5d346052016-03-08 12:56:08 -08002619 // Shell command infrastructure: run the given job immediately
2620 int executeRunCommand(String pkgName, int userId, int jobId, boolean force) {
2621 if (DEBUG) {
2622 Slog.v(TAG, "executeRunCommand(): " + pkgName + "/" + userId
2623 + " " + jobId + " f=" + force);
2624 }
2625
2626 try {
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002627 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2628 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
Christopher Tate5d346052016-03-08 12:56:08 -08002629 if (uid < 0) {
2630 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2631 }
2632
2633 synchronized (mLock) {
2634 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2635 if (js == null) {
2636 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2637 }
2638
2639 js.overrideState = (force) ? JobStatus.OVERRIDE_FULL : JobStatus.OVERRIDE_SOFT;
2640 if (!js.isConstraintsSatisfied()) {
2641 js.overrideState = 0;
2642 return JobSchedulerShellCommand.CMD_ERR_CONSTRAINTS;
2643 }
2644
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002645 queueReadyJobsForExecutionLocked();
2646 maybeRunPendingJobsLocked();
Christopher Tate5d346052016-03-08 12:56:08 -08002647 }
2648 } catch (RemoteException e) {
2649 // can't happen
2650 }
2651 return 0;
2652 }
2653
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002654 // Shell command infrastructure: immediately timeout currently executing jobs
2655 int executeTimeoutCommand(PrintWriter pw, String pkgName, int userId,
2656 boolean hasJobId, int jobId) {
2657 if (DEBUG) {
2658 Slog.v(TAG, "executeTimeoutCommand(): " + pkgName + "/" + userId + " " + jobId);
2659 }
2660
2661 synchronized (mLock) {
2662 boolean foundSome = false;
2663 for (int i=0; i<mActiveServices.size(); i++) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002664 final JobServiceContext jc = mActiveServices.get(i);
2665 final JobStatus js = jc.getRunningJobLocked();
Makoto Onukid2bfec62018-01-12 13:58:01 -08002666 if (jc.timeoutIfExecutingLocked(pkgName, userId, hasJobId, jobId, "shell")) {
Dianne Hackborneb90fa92017-06-30 14:03:36 -07002667 foundSome = true;
2668 pw.print("Timing out: ");
2669 js.printUniqueId(pw);
2670 pw.print(" ");
2671 pw.println(js.getServiceComponent().flattenToShortString());
2672 }
Dianne Hackborn83b40f62017-04-26 13:59:47 -07002673 }
2674 if (!foundSome) {
2675 pw.println("No matching executing jobs found.");
2676 }
2677 }
2678 return 0;
2679 }
2680
Christopher Tate8c67d122017-09-29 16:54:26 -07002681 // Shell command infrastructure: cancel a scheduled job
2682 int executeCancelCommand(PrintWriter pw, String pkgName, int userId,
2683 boolean hasJobId, int jobId) {
2684 if (DEBUG) {
2685 Slog.v(TAG, "executeCancelCommand(): " + pkgName + "/" + userId + " " + jobId);
2686 }
2687
2688 int pkgUid = -1;
2689 try {
2690 IPackageManager pm = AppGlobals.getPackageManager();
2691 pkgUid = pm.getPackageUid(pkgName, 0, userId);
2692 } catch (RemoteException e) { /* can't happen */ }
2693
2694 if (pkgUid < 0) {
2695 pw.println("Package " + pkgName + " not found.");
2696 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2697 }
2698
2699 if (!hasJobId) {
2700 pw.println("Canceling all jobs for " + pkgName + " in user " + userId);
2701 if (!cancelJobsForUid(pkgUid, "cancel shell command for package")) {
2702 pw.println("No matching jobs found.");
2703 }
2704 } else {
2705 pw.println("Canceling job " + pkgName + "/#" + jobId + " in user " + userId);
Makoto Onukid2bfec62018-01-12 13:58:01 -08002706 if (!cancelJob(pkgUid, jobId, Process.SHELL_UID)) {
Christopher Tate8c67d122017-09-29 16:54:26 -07002707 pw.println("No matching job found.");
2708 }
2709 }
2710
2711 return 0;
2712 }
2713
Dianne Hackborna06ec6a2017-02-13 10:08:42 -08002714 void setMonitorBattery(boolean enabled) {
2715 synchronized (mLock) {
2716 if (mBatteryController != null) {
2717 mBatteryController.getTracker().setMonitorBatteryLocked(enabled);
2718 }
2719 }
2720 }
2721
2722 int getBatterySeq() {
2723 synchronized (mLock) {
2724 return mBatteryController != null ? mBatteryController.getTracker().getSeq() : -1;
2725 }
2726 }
2727
2728 boolean getBatteryCharging() {
2729 synchronized (mLock) {
2730 return mBatteryController != null
2731 ? mBatteryController.getTracker().isOnStablePower() : false;
2732 }
2733 }
2734
2735 boolean getBatteryNotLow() {
2736 synchronized (mLock) {
2737 return mBatteryController != null
2738 ? mBatteryController.getTracker().isBatteryNotLow() : false;
2739 }
2740 }
2741
Dianne Hackborn532ea262017-03-17 17:50:55 -07002742 int getStorageSeq() {
2743 synchronized (mLock) {
2744 return mStorageController != null ? mStorageController.getTracker().getSeq() : -1;
2745 }
2746 }
2747
2748 boolean getStorageNotLow() {
2749 synchronized (mLock) {
2750 return mStorageController != null
2751 ? mStorageController.getTracker().isStorageNotLow() : false;
2752 }
2753 }
2754
Christopher Tatea732f012017-10-26 17:26:53 -07002755 long getCurrentHeartbeat() {
2756 synchronized (mLock) {
2757 return mHeartbeat;
2758 }
2759 }
2760
Christopher Tated1aebb32018-01-31 13:24:14 -08002761 // Shell command infrastructure
Dianne Hackborn6d068262017-05-16 13:14:37 -07002762 int getJobState(PrintWriter pw, String pkgName, int userId, int jobId) {
2763 try {
2764 final int uid = AppGlobals.getPackageManager().getPackageUid(pkgName, 0,
2765 userId != UserHandle.USER_ALL ? userId : UserHandle.USER_SYSTEM);
2766 if (uid < 0) {
2767 pw.print("unknown("); pw.print(pkgName); pw.println(")");
2768 return JobSchedulerShellCommand.CMD_ERR_NO_PACKAGE;
2769 }
2770
2771 synchronized (mLock) {
2772 final JobStatus js = mJobs.getJobByUidAndJobId(uid, jobId);
2773 if (DEBUG) Slog.d(TAG, "get-job-state " + uid + "/" + jobId + ": " + js);
2774 if (js == null) {
2775 pw.print("unknown("); UserHandle.formatUid(pw, uid);
2776 pw.print("/jid"); pw.print(jobId); pw.println(")");
2777 return JobSchedulerShellCommand.CMD_ERR_NO_JOB;
2778 }
2779
2780 boolean printed = false;
2781 if (mPendingJobs.contains(js)) {
2782 pw.print("pending");
2783 printed = true;
2784 }
2785 if (isCurrentlyActiveLocked(js)) {
2786 if (printed) {
2787 pw.print(" ");
2788 }
2789 printed = true;
2790 pw.println("active");
2791 }
2792 if (!ArrayUtils.contains(mStartedUsers, js.getUserId())) {
2793 if (printed) {
2794 pw.print(" ");
2795 }
2796 printed = true;
2797 pw.println("user-stopped");
2798 }
2799 if (mBackingUpUids.indexOfKey(js.getSourceUid()) >= 0) {
2800 if (printed) {
2801 pw.print(" ");
2802 }
2803 printed = true;
2804 pw.println("backing-up");
2805 }
2806 boolean componentPresent = false;
2807 try {
2808 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2809 js.getServiceComponent(),
2810 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2811 js.getUserId()) != null);
2812 } catch (RemoteException e) {
2813 }
2814 if (!componentPresent) {
2815 if (printed) {
2816 pw.print(" ");
2817 }
2818 printed = true;
2819 pw.println("no-component");
2820 }
2821 if (js.isReady()) {
2822 if (printed) {
2823 pw.print(" ");
2824 }
2825 printed = true;
2826 pw.println("ready");
2827 }
2828 if (!printed) {
2829 pw.print("waiting");
2830 }
2831 pw.println();
2832 }
2833 } catch (RemoteException e) {
2834 // can't happen
2835 }
2836 return 0;
2837 }
2838
Christopher Tated1aebb32018-01-31 13:24:14 -08002839 // Shell command infrastructure
2840 int executeHeartbeatCommand(PrintWriter pw, int numBeats) {
2841 if (numBeats < 1) {
2842 pw.println(getCurrentHeartbeat());
2843 return 0;
2844 }
2845
2846 pw.print("Advancing standby heartbeat by ");
2847 pw.println(numBeats);
2848 synchronized (mLock) {
2849 advanceHeartbeatLocked(numBeats);
2850 }
2851 return 0;
2852 }
2853
Shreyas Basarge5db09082016-01-07 13:38:29 +00002854 private String printContextIdToJobMap(JobStatus[] map, String initial) {
2855 StringBuilder s = new StringBuilder(initial + ": ");
2856 for (int i=0; i<map.length; i++) {
2857 s.append("(")
2858 .append(map[i] == null? -1: map[i].getJobId())
2859 .append(map[i] == null? -1: map[i].getUid())
2860 .append(")" );
2861 }
2862 return s.toString();
2863 }
2864
2865 private String printPendingQueue() {
2866 StringBuilder s = new StringBuilder("Pending queue: ");
2867 Iterator<JobStatus> it = mPendingJobs.iterator();
2868 while (it.hasNext()) {
2869 JobStatus js = it.next();
2870 s.append("(")
2871 .append(js.getJob().getId())
2872 .append(", ")
2873 .append(js.getUid())
2874 .append(") ");
2875 }
2876 return s.toString();
Jeff Sharkey5217cac2015-12-20 15:34:01 -07002877 }
Christopher Tate7060b042014-06-09 19:50:00 -07002878
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002879 static void dumpHelp(PrintWriter pw) {
2880 pw.println("Job Scheduler (jobscheduler) dump options:");
2881 pw.println(" [-h] [package] ...");
2882 pw.println(" -h: print this help");
2883 pw.println(" [package] is an optional package name to limit the output to.");
2884 }
2885
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002886 /** Sort jobs by caller UID, then by Job ID. */
2887 private static void sortJobs(List<JobStatus> jobs) {
2888 Collections.sort(jobs, new Comparator<JobStatus>() {
2889 @Override
2890 public int compare(JobStatus o1, JobStatus o2) {
2891 int uid1 = o1.getUid();
2892 int uid2 = o2.getUid();
2893 int id1 = o1.getJobId();
2894 int id2 = o2.getJobId();
2895 if (uid1 != uid2) {
2896 return uid1 < uid2 ? -1 : 1;
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002897 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002898 return id1 < id2 ? -1 : (id1 > id2 ? 1 : 0);
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002899 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002900 });
2901 }
Jeff Sharkeyf07c7b92016-04-22 09:50:16 -06002902
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002903 void dumpInternal(final PrintWriter pw, int filterUid) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002904 final int filterUidFinal = UserHandle.getAppId(filterUid);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -07002905 final long nowElapsed = sElapsedRealtimeClock.millis();
2906 final long nowUptime = sUptimeMillisClock.millis();
Dianne Hackborn33d31c52016-02-16 10:30:33 -08002907 synchronized (mLock) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002908 mConstants.dump(pw);
2909 pw.println();
Jeff Sharkey822cbd12016-02-25 11:09:55 -07002910 pw.println("Started users: " + Arrays.toString(mStartedUsers));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002911 pw.print("Registered ");
2912 pw.print(mJobs.size());
2913 pw.println(" jobs:");
Christopher Tate7060b042014-06-09 19:50:00 -07002914 if (mJobs.size() > 0) {
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002915 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
Kweku Adams85f2fbc2017-12-18 12:04:12 -08002916 sortJobs(jobs);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002917 for (JobStatus job : jobs) {
2918 pw.print(" JOB #"); job.printUniqueId(pw); pw.print(": ");
2919 pw.println(job.toShortStringExceptUniqueId());
2920
2921 // Skip printing details if the caller requested a filter
2922 if (!job.shouldDump(filterUidFinal)) {
2923 continue;
2924 }
2925
Dianne Hackborn6d068262017-05-16 13:14:37 -07002926 job.dump(pw, " ", true, nowElapsed);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002927 pw.print(" Ready: ");
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002928 pw.print(isReadyToBeExecutedLocked(job));
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002929 pw.print(" (job=");
2930 pw.print(job.isReady());
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002931 pw.print(" user=");
2932 pw.print(ArrayUtils.contains(mStartedUsers, job.getUserId()));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002933 pw.print(" !pending=");
2934 pw.print(!mPendingJobs.contains(job));
2935 pw.print(" !active=");
2936 pw.print(!isCurrentlyActiveLocked(job));
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002937 pw.print(" !backingup=");
2938 pw.print(!(mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0));
Dianne Hackborn0aa43132017-03-22 11:42:03 -07002939 pw.print(" comp=");
2940 boolean componentPresent = false;
2941 try {
2942 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
2943 job.getServiceComponent(),
2944 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
2945 job.getUserId()) != null);
2946 } catch (RemoteException e) {
2947 }
2948 pw.print(componentPresent);
Dianne Hackborne9a988c2016-05-27 17:59:40 -07002949 pw.println(")");
2950 }
Christopher Tate7060b042014-06-09 19:50:00 -07002951 } else {
Christopher Tatef973a7b2014-08-29 12:54:08 -07002952 pw.println(" None.");
Christopher Tate7060b042014-06-09 19:50:00 -07002953 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -07002954 for (int i=0; i<mControllers.size(); i++) {
Christopher Tate7060b042014-06-09 19:50:00 -07002955 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002956 mControllers.get(i).dumpControllerStateLocked(pw, filterUidFinal);
Christopher Tate7060b042014-06-09 19:50:00 -07002957 }
2958 pw.println();
Dianne Hackborn970510b2016-02-24 16:56:42 -08002959 pw.println("Uid priority overrides:");
2960 for (int i=0; i< mUidPriorityOverride.size(); i++) {
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002961 int uid = mUidPriorityOverride.keyAt(i);
2962 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2963 pw.print(" "); pw.print(UserHandle.formatUid(uid));
2964 pw.print(": "); pw.println(mUidPriorityOverride.valueAt(i));
2965 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002966 }
Dianne Hackbornf9bac162017-04-20 17:17:48 -07002967 if (mBackingUpUids.size() > 0) {
2968 pw.println();
2969 pw.println("Backing up uids:");
2970 boolean first = true;
2971 for (int i = 0; i < mBackingUpUids.size(); i++) {
2972 int uid = mBackingUpUids.keyAt(i);
2973 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
2974 if (first) {
2975 pw.print(" ");
2976 first = false;
2977 } else {
2978 pw.print(", ");
2979 }
2980 pw.print(UserHandle.formatUid(uid));
2981 }
2982 }
2983 pw.println();
2984 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002985 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002986 mJobPackageTracker.dump(pw, "", filterUidFinal);
Dianne Hackborn807de782016-04-07 17:54:41 -07002987 pw.println();
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07002988 if (mJobPackageTracker.dumpHistory(pw, "", filterUidFinal)) {
2989 pw.println();
2990 }
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002991 pw.println("Pending queue:");
2992 for (int i=0; i<mPendingJobs.size(); i++) {
2993 JobStatus job = mPendingJobs.get(i);
2994 pw.print(" Pending #"); pw.print(i); pw.print(": ");
2995 pw.println(job.toShortString());
Dianne Hackborn6d068262017-05-16 13:14:37 -07002996 job.dump(pw, " ", false, nowElapsed);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08002997 int priority = evaluateJobPriorityLocked(job);
2998 if (priority != JobInfo.PRIORITY_DEFAULT) {
2999 pw.print(" Evaluated priority: "); pw.println(priority);
3000 }
3001 pw.print(" Tag: "); pw.println(job.getTag());
Christopher Tate7234fc62017-04-03 17:36:07 -07003002 pw.print(" Enq: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003003 TimeUtils.formatDuration(job.madePending - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003004 pw.println();
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003005 }
Christopher Tate7060b042014-06-09 19:50:00 -07003006 pw.println();
3007 pw.println("Active jobs:");
Dianne Hackbornfdb19562014-07-11 16:03:36 -07003008 for (int i=0; i<mActiveServices.size(); i++) {
3009 JobServiceContext jsc = mActiveServices.get(i);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003010 pw.print(" Slot #"); pw.print(i); pw.print(": ");
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003011 final JobStatus job = jsc.getRunningJobLocked();
Christopher Tate7234fc62017-04-03 17:36:07 -07003012 if (job == null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -07003013 if (jsc.mStoppedReason != null) {
3014 pw.print("inactive since ");
3015 TimeUtils.formatDuration(jsc.mStoppedTime, nowElapsed, pw);
3016 pw.print(", stopped because: ");
3017 pw.println(jsc.mStoppedReason);
3018 } else {
3019 pw.println("inactive");
3020 }
Christopher Tate7060b042014-06-09 19:50:00 -07003021 continue;
3022 } else {
Christopher Tate7234fc62017-04-03 17:36:07 -07003023 pw.println(job.toShortString());
Dianne Hackborn970510b2016-02-24 16:56:42 -08003024 pw.print(" Running for: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003025 TimeUtils.formatDuration(nowElapsed - jsc.getExecutionStartTimeElapsed(), pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003026 pw.print(", timeout at: ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003027 TimeUtils.formatDuration(jsc.getTimeoutElapsed() - nowElapsed, pw);
Dianne Hackborn970510b2016-02-24 16:56:42 -08003028 pw.println();
Dianne Hackborn6d068262017-05-16 13:14:37 -07003029 job.dump(pw, " ", false, nowElapsed);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003030 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003031 if (priority != JobInfo.PRIORITY_DEFAULT) {
Dianne Hackborn970510b2016-02-24 16:56:42 -08003032 pw.print(" Evaluated priority: "); pw.println(priority);
Dianne Hackborn1085ff62016-02-23 17:04:58 -08003033 }
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003034 pw.print(" Active at ");
Dianne Hackborn6d068262017-05-16 13:14:37 -07003035 TimeUtils.formatDuration(job.madeActive - nowUptime, pw);
Dianne Hackbornbfc23312017-05-11 11:53:24 -07003036 pw.print(", pending for ");
Christopher Tate7234fc62017-04-03 17:36:07 -07003037 TimeUtils.formatDuration(job.madeActive - job.madePending, pw);
3038 pw.println();
Christopher Tate7060b042014-06-09 19:50:00 -07003039 }
3040 }
Dianne Hackbornef3aa6e2016-04-29 18:18:08 -07003041 if (filterUid == -1) {
3042 pw.println();
3043 pw.print("mReadyToRock="); pw.println(mReadyToRock);
3044 pw.print("mReportedActive="); pw.println(mReportedActive);
3045 pw.print("mMaxActiveJobs="); pw.println(mMaxActiveJobs);
3046 }
Makoto Onukie7b02982017-08-24 14:23:36 -07003047 pw.println();
3048 pw.print("PersistStats: ");
3049 pw.println(mJobs.getPersistStats());
Christopher Tate7060b042014-06-09 19:50:00 -07003050 }
3051 pw.println();
3052 }
Kweku Adams85f2fbc2017-12-18 12:04:12 -08003053
3054 void dumpInternalProto(final FileDescriptor fd, int filterUid) {
3055 ProtoOutputStream proto = new ProtoOutputStream(fd);
3056 final int filterUidFinal = UserHandle.getAppId(filterUid);
3057 final long nowElapsed = sElapsedRealtimeClock.millis();
3058 final long nowUptime = sUptimeMillisClock.millis();
3059
3060 synchronized (mLock) {
3061 mConstants.dump(proto, JobSchedulerServiceDumpProto.SETTINGS);
3062 for (int u : mStartedUsers) {
3063 proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
3064 }
3065 if (mJobs.size() > 0) {
3066 final List<JobStatus> jobs = mJobs.mJobSet.getAllJobs();
3067 sortJobs(jobs);
3068 for (JobStatus job : jobs) {
3069 final long rjToken = proto.start(JobSchedulerServiceDumpProto.REGISTERED_JOBS);
3070 job.writeToShortProto(proto, JobSchedulerServiceDumpProto.RegisteredJob.INFO);
3071
3072 // Skip printing details if the caller requested a filter
3073 if (!job.shouldDump(filterUidFinal)) {
3074 continue;
3075 }
3076
3077 job.dump(proto, JobSchedulerServiceDumpProto.RegisteredJob.DUMP, true, nowElapsed);
3078
3079 // isReadyToBeExecuted
3080 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_READY,
3081 job.isReady());
3082 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_USER_STARTED,
3083 ArrayUtils.contains(mStartedUsers, job.getUserId()));
3084 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
3085 mPendingJobs.contains(job));
3086 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
3087 isCurrentlyActiveLocked(job));
3088 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_UID_BACKING_UP,
3089 mBackingUpUids.indexOfKey(job.getSourceUid()) >= 0);
3090 boolean componentPresent = false;
3091 try {
3092 componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
3093 job.getServiceComponent(),
3094 PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
3095 job.getUserId()) != null);
3096 } catch (RemoteException e) {
3097 }
3098 proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_PRESENT,
3099 componentPresent);
3100
3101 proto.end(rjToken);
3102 }
3103 }
3104 for (StateController controller : mControllers) {
3105 controller.dumpControllerStateLocked(
3106 proto, JobSchedulerServiceDumpProto.CONTROLLERS, filterUidFinal);
3107 }
3108 for (int i=0; i< mUidPriorityOverride.size(); i++) {
3109 int uid = mUidPriorityOverride.keyAt(i);
3110 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3111 long pToken = proto.start(JobSchedulerServiceDumpProto.PRIORITY_OVERRIDES);
3112 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.UID, uid);
3113 proto.write(JobSchedulerServiceDumpProto.PriorityOverride.OVERRIDE_VALUE,
3114 mUidPriorityOverride.valueAt(i));
3115 proto.end(pToken);
3116 }
3117 }
3118 for (int i = 0; i < mBackingUpUids.size(); i++) {
3119 int uid = mBackingUpUids.keyAt(i);
3120 if (filterUidFinal == -1 || filterUidFinal == UserHandle.getAppId(uid)) {
3121 proto.write(JobSchedulerServiceDumpProto.BACKING_UP_UIDS, uid);
3122 }
3123 }
3124
3125 mJobPackageTracker.dump(proto, JobSchedulerServiceDumpProto.PACKAGE_TRACKER,
3126 filterUidFinal);
3127 mJobPackageTracker.dumpHistory(proto, JobSchedulerServiceDumpProto.HISTORY,
3128 filterUidFinal);
3129
3130 for (JobStatus job : mPendingJobs) {
3131 final long pjToken = proto.start(JobSchedulerServiceDumpProto.PENDING_JOBS);
3132
3133 job.writeToShortProto(proto, PendingJob.INFO);
3134 job.dump(proto, PendingJob.DUMP, false, nowElapsed);
3135 int priority = evaluateJobPriorityLocked(job);
3136 if (priority != JobInfo.PRIORITY_DEFAULT) {
3137 proto.write(PendingJob.EVALUATED_PRIORITY, priority);
3138 }
3139 proto.write(PendingJob.ENQUEUED_DURATION_MS, nowUptime - job.madePending);
3140
3141 proto.end(pjToken);
3142 }
3143 for (JobServiceContext jsc : mActiveServices) {
3144 final long ajToken = proto.start(JobSchedulerServiceDumpProto.ACTIVE_JOBS);
3145 final JobStatus job = jsc.getRunningJobLocked();
3146
3147 if (job == null) {
3148 final long ijToken = proto.start(ActiveJob.INACTIVE);
3149
3150 proto.write(ActiveJob.InactiveJob.TIME_SINCE_STOPPED_MS,
3151 nowElapsed - jsc.mStoppedTime);
3152 if (jsc.mStoppedReason != null) {
3153 proto.write(ActiveJob.InactiveJob.STOPPED_REASON,
3154 jsc.mStoppedReason);
3155 }
3156
3157 proto.end(ijToken);
3158 } else {
3159 final long rjToken = proto.start(ActiveJob.RUNNING);
3160
3161 job.writeToShortProto(proto, ActiveJob.RunningJob.INFO);
3162
3163 proto.write(ActiveJob.RunningJob.RUNNING_DURATION_MS,
3164 nowElapsed - jsc.getExecutionStartTimeElapsed());
3165 proto.write(ActiveJob.RunningJob.TIME_UNTIL_TIMEOUT_MS,
3166 jsc.getTimeoutElapsed() - nowElapsed);
3167
3168 job.dump(proto, ActiveJob.RunningJob.DUMP, false, nowElapsed);
3169
3170 int priority = evaluateJobPriorityLocked(jsc.getRunningJobLocked());
3171 if (priority != JobInfo.PRIORITY_DEFAULT) {
3172 proto.write(ActiveJob.RunningJob.EVALUATED_PRIORITY, priority);
3173 }
3174
3175 proto.write(ActiveJob.RunningJob.TIME_SINCE_MADE_ACTIVE_MS,
3176 nowUptime - job.madeActive);
3177 proto.write(ActiveJob.RunningJob.PENDING_DURATION_MS,
3178 job.madeActive - job.madePending);
3179
3180 proto.end(rjToken);
3181 }
3182 proto.end(ajToken);
3183 }
3184 if (filterUid == -1) {
3185 proto.write(JobSchedulerServiceDumpProto.IS_READY_TO_ROCK, mReadyToRock);
3186 proto.write(JobSchedulerServiceDumpProto.REPORTED_ACTIVE, mReportedActive);
3187 proto.write(JobSchedulerServiceDumpProto.MAX_ACTIVE_JOBS, mMaxActiveJobs);
3188 }
3189 }
3190
3191 proto.flush();
3192 }
Christopher Tate7060b042014-06-09 19:50:00 -07003193}