blob: b089ba7ee9ed8720da0e4efeb209e0e46fb6007d [file] [log] [blame]
Matthew Williams6de79e22014-05-01 10:47: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
Christopher Tate7060b042014-06-09 19:50:00 -070017package com.android.server.job;
Matthew Williams6de79e22014-05-01 10:47:00 -070018
Matthew Williams691e93e2014-05-12 15:33:09 -070019import android.app.ActivityManager;
Christopher Tate7060b042014-06-09 19:50:00 -070020import android.app.job.JobParameters;
21import android.app.job.IJobCallback;
22import android.app.job.IJobService;
Matthew Williams6de79e22014-05-01 10:47:00 -070023import android.content.ComponentName;
Matthew Williams691e93e2014-05-12 15:33:09 -070024import android.content.Context;
25import android.content.Intent;
Matthew Williams6de79e22014-05-01 10:47:00 -070026import android.content.ServiceConnection;
Shreyas Basarge968ac752016-01-11 23:09:26 +000027import android.content.pm.PackageManager;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080028import android.net.Uri;
Matthew Williams9b9244b62014-05-14 11:06:04 -070029import android.os.Binder;
Matthew Williams691e93e2014-05-12 15:33:09 -070030import android.os.Handler;
Matthew Williams6de79e22014-05-01 10:47:00 -070031import android.os.IBinder;
Matthew Williams691e93e2014-05-12 15:33:09 -070032import android.os.Looper;
33import android.os.Message;
34import android.os.PowerManager;
35import android.os.RemoteException;
Matthew Williamseffacfa2014-06-05 20:56:40 -070036import android.os.SystemClock;
Matthew Williams691e93e2014-05-12 15:33:09 -070037import android.os.UserHandle;
38import android.os.WorkSource;
Matthew Williams691e93e2014-05-12 15:33:09 -070039import android.util.Slog;
Matthew Williams6de79e22014-05-01 10:47:00 -070040
Matthew Williams9b9244b62014-05-14 11:06:04 -070041import com.android.internal.annotations.GuardedBy;
42import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070043import com.android.internal.app.IBatteryStats;
Christopher Tate7060b042014-06-09 19:50:00 -070044import com.android.server.job.controllers.JobStatus;
Matthew Williams6de79e22014-05-01 10:47:00 -070045
Matthew Williams691e93e2014-05-12 15:33:09 -070046import java.util.concurrent.atomic.AtomicBoolean;
47
Matthew Williams6de79e22014-05-01 10:47:00 -070048/**
Matthew Williamsee410da2014-07-25 11:30:40 -070049 * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
50 * class.
51 *
52 * There are two important interactions into this class from the
53 * {@link com.android.server.job.JobSchedulerService}. To execute a job and to cancel a job.
54 * - Execution of a new job is handled by the {@link #mAvailable}. This bit is flipped once when a
55 * job lands, and again when it is complete.
56 * - Cancelling is trickier, because there are also interactions from the client. It's possible
57 * the {@link com.android.server.job.JobServiceContext.JobServiceHandler} tries to process a
58 * {@link #MSG_CANCEL} after the client has already finished. This is handled by having
59 * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelH} check whether
60 * the context is still valid.
61 * To mitigate this, tearing down the context removes all messages from the handler, including any
62 * tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob()
63 * calls to the client after they've specified jobFinished().
Matthew Williams6de79e22014-05-01 10:47:00 -070064 */
Christopher Tate7060b042014-06-09 19:50:00 -070065public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
Matthew Williamsfa8e5082015-10-15 15:59:12 -070066 private static final boolean DEBUG = JobSchedulerService.DEBUG;
Christopher Tate7060b042014-06-09 19:50:00 -070067 private static final String TAG = "JobServiceContext";
68 /** Define the maximum # of jobs allowed to run on a service at once. */
69 private static final int defaultMaxActiveJobsPerService =
Matthew Williams691e93e2014-05-12 15:33:09 -070070 ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
Christopher Tate7060b042014-06-09 19:50:00 -070071 /** Amount of time a job is allowed to execute for before being considered timed-out. */
Matthew Williams1bde39a2015-10-07 14:29:30 -070072 private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
Christopher Tate7060b042014-06-09 19:50:00 -070073 /** Amount of time the JobScheduler will wait for a response from an app for a message. */
Matthew Williams691e93e2014-05-12 15:33:09 -070074 private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
Matthew Williams6de79e22014-05-01 10:47:00 -070075
Matthew Williams691e93e2014-05-12 15:33:09 -070076 private static final String[] VERB_STRINGS = {
riddle_hsu6a94aba2015-05-01 01:52:58 +080077 "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
Matthew Williams691e93e2014-05-12 15:33:09 -070078 };
79
Christopher Tate7060b042014-06-09 19:50:00 -070080 // States that a job occupies while interacting with the client.
Matthew Williams9b9244b62014-05-14 11:06:04 -070081 static final int VERB_BINDING = 0;
82 static final int VERB_STARTING = 1;
83 static final int VERB_EXECUTING = 2;
84 static final int VERB_STOPPING = 3;
riddle_hsu6a94aba2015-05-01 01:52:58 +080085 static final int VERB_FINISHED = 4;
Matthew Williams691e93e2014-05-12 15:33:09 -070086
87 // Messages that result from interactions with the client service.
88 /** System timed out waiting for a response. */
89 private static final int MSG_TIMEOUT = 0;
90 /** Received a callback from client. */
91 private static final int MSG_CALLBACK = 1;
Christopher Tate7060b042014-06-09 19:50:00 -070092 /** Run through list and start any ready jobs.*/
Matthew Williams9b9244b62014-05-14 11:06:04 -070093 private static final int MSG_SERVICE_BOUND = 2;
Christopher Tate7060b042014-06-09 19:50:00 -070094 /** Cancel a job. */
Matthew Williams691e93e2014-05-12 15:33:09 -070095 private static final int MSG_CANCEL = 3;
Christopher Tate7060b042014-06-09 19:50:00 -070096 /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
Matthew Williams9b9244b62014-05-14 11:06:04 -070097 private static final int MSG_SHUTDOWN_EXECUTION = 4;
Matthew Williams691e93e2014-05-12 15:33:09 -070098
Shreyas Basarge5db09082016-01-07 13:38:29 +000099 public static final int NO_PREFERRED_UID = -1;
100
Matthew Williams691e93e2014-05-12 15:33:09 -0700101 private final Handler mCallbackHandler;
Christopher Tate7060b042014-06-09 19:50:00 -0700102 /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
103 private final JobCompletedListener mCompletedListener;
Matthew Williams691e93e2014-05-12 15:33:09 -0700104 /** Used for service binding, etc. */
105 private final Context mContext;
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800106 private final Object mLock;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700107 private final IBatteryStats mBatteryStats;
Dianne Hackborn807de782016-04-07 17:54:41 -0700108 private final JobPackageTracker mJobPackageTracker;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700109 private PowerManager.WakeLock mWakeLock;
Matthew Williams6de79e22014-05-01 10:47:00 -0700110
Matthew Williams9b9244b62014-05-14 11:06:04 -0700111 // Execution state.
Christopher Tate7060b042014-06-09 19:50:00 -0700112 private JobParameters mParams;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700113 @VisibleForTesting
114 int mVerb;
115 private AtomicBoolean mCancelled = new AtomicBoolean();
Matthew Williams6de79e22014-05-01 10:47:00 -0700116
Matthew Williams0cc76542015-10-16 21:04:51 -0700117 /**
118 * All the information maintained about the job currently being executed.
119 *
120 * Any reads (dereferences) not done from the handler thread must be synchronized on
121 * {@link #mLock}.
122 * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
123 */
Christopher Tate7060b042014-06-09 19:50:00 -0700124 private JobStatus mRunningJob;
Shreyas Basarge5db09082016-01-07 13:38:29 +0000125 /** Used to store next job to run when current job is to be preempted. */
126 private int mPreferredUid;
Christopher Tate7060b042014-06-09 19:50:00 -0700127 IJobService service;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700128
Matthew Williamsee410da2014-07-25 11:30:40 -0700129 /**
130 * Whether this context is free. This is set to false at the start of execution, and reset to
131 * true when execution is complete.
132 */
Matthew Williamseffacfa2014-06-05 20:56:40 -0700133 @GuardedBy("mLock")
Matthew Williams9b9244b62014-05-14 11:06:04 -0700134 private boolean mAvailable;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700135 /** Track start time. */
136 private long mExecutionStartTimeElapsed;
137 /** Track when job will timeout. */
138 private long mTimeoutElapsed;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700139
Dianne Hackborn807de782016-04-07 17:54:41 -0700140 JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
141 JobPackageTracker tracker, Looper looper) {
142 this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700143 }
144
145 @VisibleForTesting
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800146 JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
Dianne Hackborn807de782016-04-07 17:54:41 -0700147 JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700148 mContext = context;
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800149 mLock = lock;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700150 mBatteryStats = batteryStats;
Dianne Hackborn807de782016-04-07 17:54:41 -0700151 mJobPackageTracker = tracker;
Christopher Tate7060b042014-06-09 19:50:00 -0700152 mCallbackHandler = new JobServiceHandler(looper);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700153 mCompletedListener = completedListener;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700154 mAvailable = true;
Shreyas Basarge5db09082016-01-07 13:38:29 +0000155 mVerb = VERB_FINISHED;
156 mPreferredUid = NO_PREFERRED_UID;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700157 }
158
159 /**
Shreyas Basarge5db09082016-01-07 13:38:29 +0000160 * Give a job to this context for execution. Callers must first check {@link #getRunningJob()}
161 * and ensure it is null to make sure this is a valid context.
Christopher Tate7060b042014-06-09 19:50:00 -0700162 * @param job The status of the job that we are going to run.
163 * @return True if the job is valid and is running. False if the job cannot be executed.
Matthew Williams9b9244b62014-05-14 11:06:04 -0700164 */
Christopher Tate7060b042014-06-09 19:50:00 -0700165 boolean executeRunnableJob(JobStatus job) {
Matthew Williamseffacfa2014-06-05 20:56:40 -0700166 synchronized (mLock) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700167 if (!mAvailable) {
168 Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
169 return false;
170 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700171
Shreyas Basarge5db09082016-01-07 13:38:29 +0000172 mPreferredUid = NO_PREFERRED_UID;
173
Christopher Tate7060b042014-06-09 19:50:00 -0700174 mRunningJob = job;
Matthew Williams673ef9e2015-04-03 21:52:02 -0700175 final boolean isDeadlineExpired =
Matthew Williamsd9db9d72015-05-14 17:47:33 -0700176 job.hasDeadlineConstraint() &&
177 (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800178 Uri[] triggeredUris = null;
179 if (job.changedUris != null) {
180 triggeredUris = new Uri[job.changedUris.size()];
181 job.changedUris.toArray(triggeredUris);
182 }
183 String[] triggeredAuthorities = null;
184 if (job.changedAuthorities != null) {
185 triggeredAuthorities = new String[job.changedAuthorities.size()];
186 job.changedAuthorities.toArray(triggeredAuthorities);
187 }
Dianne Hackbornba604732016-02-10 17:05:10 -0800188 mParams = new JobParameters(this, job.getJobId(), job.getExtras(),
189 job.getTransientExtras(), isDeadlineExpired,
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800190 triggeredUris, triggeredAuthorities);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700191 mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700192
Matthew Williamseffacfa2014-06-05 20:56:40 -0700193 mVerb = VERB_BINDING;
Matthew Williams75fc5252014-09-02 16:17:53 -0700194 scheduleOpTimeOut();
Christopher Tate7060b042014-06-09 19:50:00 -0700195 final Intent intent = new Intent().setComponent(job.getServiceComponent());
Matthew Williamseffacfa2014-06-05 20:56:40 -0700196 boolean binding = mContext.bindServiceAsUser(intent, this,
197 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
Christopher Tate7060b042014-06-09 19:50:00 -0700198 new UserHandle(job.getUserId()));
Matthew Williamseffacfa2014-06-05 20:56:40 -0700199 if (!binding) {
200 if (DEBUG) {
Christopher Tate7060b042014-06-09 19:50:00 -0700201 Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
Matthew Williamseffacfa2014-06-05 20:56:40 -0700202 }
Christopher Tate7060b042014-06-09 19:50:00 -0700203 mRunningJob = null;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700204 mParams = null;
205 mExecutionStartTimeElapsed = 0L;
riddle_hsu6a94aba2015-05-01 01:52:58 +0800206 mVerb = VERB_FINISHED;
Matthew Williams7ac52d52014-09-12 14:40:18 -0700207 removeOpTimeOut();
Matthew Williamseffacfa2014-06-05 20:56:40 -0700208 return false;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700209 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700210 try {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800211 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700212 } catch (RemoteException e) {
213 // Whatever.
214 }
Dianne Hackborn807de782016-04-07 17:54:41 -0700215 mJobPackageTracker.noteActive(job);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700216 mAvailable = false;
217 return true;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700218 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700219 }
220
Matthew Williamsee410da2014-07-25 11:30:40 -0700221 /**
222 * Used externally to query the running job. Will return null if there is no job running.
223 * Be careful when using this function, at any moment it's possible that the job returned may
224 * stop executing.
225 */
Christopher Tate7060b042014-06-09 19:50:00 -0700226 JobStatus getRunningJob() {
Christopher Tate3eddecc2016-05-25 10:44:24 -0700227 final JobStatus job;
Matthew Williamsee410da2014-07-25 11:30:40 -0700228 synchronized (mLock) {
Christopher Tate3eddecc2016-05-25 10:44:24 -0700229 job = mRunningJob;
Matthew Williamsee410da2014-07-25 11:30:40 -0700230 }
Christopher Tate3eddecc2016-05-25 10:44:24 -0700231 return job == null ? null : new JobStatus(job);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700232 }
233
Christopher Tateeafb5352016-10-04 16:34:48 -0700234 /**
235 * Internal non-cloning inspection of the currently running job, if any. The lock
236 * must be held when calling this *and* for the entire lifetime of using its returned
237 * JobStatus object!
238 */
239 JobStatus getRunningJobUnsafeLocked() {
240 return mRunningJob;
241 }
242
Christopher Tate7060b042014-06-09 19:50:00 -0700243 /** Called externally when a job that was scheduled for execution should be cancelled. */
Shreyas Basarge5db09082016-01-07 13:38:29 +0000244 void cancelExecutingJob(int reason) {
245 mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700246 }
247
Shreyas Basarge5db09082016-01-07 13:38:29 +0000248 void preemptExecutingJob() {
249 Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
250 m.arg1 = JobParameters.REASON_PREEMPT;
251 m.sendToTarget();
252 }
253
254 int getPreferredUid() {
255 return mPreferredUid;
256 }
257
258 void clearPreferredUid() {
259 mPreferredUid = NO_PREFERRED_UID;
Matthew Williams6de79e22014-05-01 10:47:00 -0700260 }
261
Matthew Williamseffacfa2014-06-05 20:56:40 -0700262 long getExecutionStartTimeElapsed() {
263 return mExecutionStartTimeElapsed;
264 }
265
266 long getTimeoutElapsed() {
267 return mTimeoutElapsed;
268 }
269
Matthew Williams6de79e22014-05-01 10:47:00 -0700270 @Override
Christopher Tate7060b042014-06-09 19:50:00 -0700271 public void jobFinished(int jobId, boolean reschedule) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700272 if (!verifyCallingUid()) {
273 return;
274 }
Christopher Tate7060b042014-06-09 19:50:00 -0700275 mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
Matthew Williams691e93e2014-05-12 15:33:09 -0700276 .sendToTarget();
Matthew Williams6de79e22014-05-01 10:47:00 -0700277 }
278
279 @Override
Christopher Tate7060b042014-06-09 19:50:00 -0700280 public void acknowledgeStopMessage(int jobId, boolean reschedule) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700281 if (!verifyCallingUid()) {
282 return;
283 }
Christopher Tate7060b042014-06-09 19:50:00 -0700284 mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
Matthew Williams691e93e2014-05-12 15:33:09 -0700285 .sendToTarget();
Matthew Williams6de79e22014-05-01 10:47:00 -0700286 }
287
288 @Override
Christopher Tate7060b042014-06-09 19:50:00 -0700289 public void acknowledgeStartMessage(int jobId, boolean ongoing) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700290 if (!verifyCallingUid()) {
291 return;
292 }
Christopher Tate7060b042014-06-09 19:50:00 -0700293 mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
Matthew Williams691e93e2014-05-12 15:33:09 -0700294 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700295
Matthew Williams691e93e2014-05-12 15:33:09 -0700296 /**
Matthew Williams9b9244b62014-05-14 11:06:04 -0700297 * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
Matthew Williams691e93e2014-05-12 15:33:09 -0700298 * we intend to send to the client - we stop sending work when the service is unbound so until
299 * then we keep the wakelock.
Matthew Williams9b9244b62014-05-14 11:06:04 -0700300 * @param name The concrete component name of the service that has been connected.
Matthew Williams691e93e2014-05-12 15:33:09 -0700301 * @param service The IBinder of the Service's communication channel,
302 */
Matthew Williams6de79e22014-05-01 10:47:00 -0700303 @Override
304 public void onServiceConnected(ComponentName name, IBinder service) {
Matthew Williams0cc76542015-10-16 21:04:51 -0700305 JobStatus runningJob;
306 synchronized (mLock) {
307 // This isn't strictly necessary b/c the JobServiceHandler is running on the main
308 // looper and at this point we can't get any binder callbacks from the client. Better
309 // safe than sorry.
310 runningJob = mRunningJob;
311 }
312 if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700313 mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
314 return;
315 }
Christopher Tate7060b042014-06-09 19:50:00 -0700316 this.service = IJobService.Stub.asInterface(service);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700317 final PowerManager pm =
318 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
Christopher Tate4b17e982016-09-26 12:59:10 -0700319 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
320 runningJob.getTag());
321 wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
322 wl.setReferenceCounted(false);
323 wl.acquire();
324 synchronized (mLock) {
325 // We use a new wakelock instance per job. In rare cases there is a race between
326 // teardown following job completion/cancellation and new job service spin-up
327 // such that if we simply assign mWakeLock to be the new instance, we orphan
328 // the currently-live lock instead of cleanly replacing it. Watch for this and
329 // explicitly fast-forward the release if we're in that situation.
330 if (mWakeLock != null) {
331 Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
332 + " tag=" + mWakeLock.getTag());
333 mWakeLock.release();
334 }
335 mWakeLock = wl;
336 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700337 mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
Matthew Williams6de79e22014-05-01 10:47:00 -0700338 }
339
Matthew Williamsee410da2014-07-25 11:30:40 -0700340 /** If the client service crashes we reschedule this job and clean up. */
Matthew Williams6de79e22014-05-01 10:47:00 -0700341 @Override
342 public void onServiceDisconnected(ComponentName name) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700343 mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
Matthew Williams691e93e2014-05-12 15:33:09 -0700344 }
345
346 /**
Matthew Williams9b9244b62014-05-14 11:06:04 -0700347 * This class is reused across different clients, and passes itself in as a callback. Check
348 * whether the client exercising the callback is the client we expect.
349 * @return True if the binder calling is coming from the client we expect.
Matthew Williams691e93e2014-05-12 15:33:09 -0700350 */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700351 private boolean verifyCallingUid() {
Matthew Williams0cc76542015-10-16 21:04:51 -0700352 synchronized (mLock) {
353 if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
354 if (DEBUG) {
355 Slog.d(TAG, "Stale callback received, ignoring.");
356 }
357 return false;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700358 }
Matthew Williams0cc76542015-10-16 21:04:51 -0700359 return true;
Matthew Williams691e93e2014-05-12 15:33:09 -0700360 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700361 }
362
363 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700364 * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this
Matthew Williams691e93e2014-05-12 15:33:09 -0700365 * class is to append 'H' to each function name that can only be called on this handler. This
366 * isn't strictly necessary because all of these functions are private, but helps clarity.
367 */
Christopher Tate7060b042014-06-09 19:50:00 -0700368 private class JobServiceHandler extends Handler {
369 JobServiceHandler(Looper looper) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700370 super(looper);
371 }
372
373 @Override
374 public void handleMessage(Message message) {
375 switch (message.what) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700376 case MSG_SERVICE_BOUND:
Matthew Williams7ac52d52014-09-12 14:40:18 -0700377 removeOpTimeOut();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700378 handleServiceBoundH();
Matthew Williams691e93e2014-05-12 15:33:09 -0700379 break;
380 case MSG_CALLBACK:
Matthew Williams9b9244b62014-05-14 11:06:04 -0700381 if (DEBUG) {
riddle_hsu6a94aba2015-05-01 01:52:58 +0800382 Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
383 + " v:" + VERB_STRINGS[mVerb]);
Matthew Williams691e93e2014-05-12 15:33:09 -0700384 }
Matthew Williams7ac52d52014-09-12 14:40:18 -0700385 removeOpTimeOut();
Matthew Williams691e93e2014-05-12 15:33:09 -0700386
Matthew Williams9b9244b62014-05-14 11:06:04 -0700387 if (mVerb == VERB_STARTING) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700388 final boolean workOngoing = message.arg2 == 1;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700389 handleStartedH(workOngoing);
390 } else if (mVerb == VERB_EXECUTING ||
391 mVerb == VERB_STOPPING) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700392 final boolean reschedule = message.arg2 == 1;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700393 handleFinishedH(reschedule);
Matthew Williams691e93e2014-05-12 15:33:09 -0700394 } else {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700395 if (DEBUG) {
Christopher Tate7060b042014-06-09 19:50:00 -0700396 Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
Matthew Williams691e93e2014-05-12 15:33:09 -0700397 }
398 }
399 break;
400 case MSG_CANCEL:
Shreyas Basarge7d089052016-02-22 22:29:39 +0000401 if (mVerb == VERB_FINISHED) {
402 if (DEBUG) {
403 Slog.d(TAG,
404 "Trying to process cancel for torn-down context, ignoring.");
405 }
406 return;
407 }
Shreyas Basarge5db09082016-01-07 13:38:29 +0000408 mParams.setStopReason(message.arg1);
409 if (message.arg1 == JobParameters.REASON_PREEMPT) {
410 mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
411 NO_PREFERRED_UID;
412 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700413 handleCancelH();
Matthew Williams691e93e2014-05-12 15:33:09 -0700414 break;
415 case MSG_TIMEOUT:
Matthew Williams9b9244b62014-05-14 11:06:04 -0700416 handleOpTimeoutH();
Matthew Williams691e93e2014-05-12 15:33:09 -0700417 break;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700418 case MSG_SHUTDOWN_EXECUTION:
Christopher Tate7060b042014-06-09 19:50:00 -0700419 closeAndCleanupJobH(true /* needsReschedule */);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700420 break;
Matthew Williams691e93e2014-05-12 15:33:09 -0700421 default:
Matthew Williamsee410da2014-07-25 11:30:40 -0700422 Slog.e(TAG, "Unrecognised message: " + message);
Matthew Williams691e93e2014-05-12 15:33:09 -0700423 }
424 }
425
Christopher Tate7060b042014-06-09 19:50:00 -0700426 /** Start the job on the service. */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700427 private void handleServiceBoundH() {
Matthew Williams75fc5252014-09-02 16:17:53 -0700428 if (DEBUG) {
429 Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
430 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700431 if (mVerb != VERB_BINDING) {
Christopher Tate7060b042014-06-09 19:50:00 -0700432 Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
Matthew Williams9b9244b62014-05-14 11:06:04 -0700433 + VERB_STRINGS[mVerb]);
Christopher Tate7060b042014-06-09 19:50:00 -0700434 closeAndCleanupJobH(false /* reschedule */);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700435 return;
436 }
437 if (mCancelled.get()) {
438 if (DEBUG) {
Christopher Tate7060b042014-06-09 19:50:00 -0700439 Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
440 + mRunningJob);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700441 }
Christopher Tate7060b042014-06-09 19:50:00 -0700442 closeAndCleanupJobH(true /* reschedule */);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700443 return;
444 }
445 try {
446 mVerb = VERB_STARTING;
447 scheduleOpTimeOut();
Christopher Tate7060b042014-06-09 19:50:00 -0700448 service.startJob(mParams);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700449 } catch (RemoteException e) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700450 Slog.e(TAG, "Error sending onStart message to '" +
Christopher Tate7060b042014-06-09 19:50:00 -0700451 mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700452 }
453 }
454
Matthew Williams691e93e2014-05-12 15:33:09 -0700455 /**
456 * State behaviours.
Christopher Tate7060b042014-06-09 19:50:00 -0700457 * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
Matthew Williams691e93e2014-05-12 15:33:09 -0700458 * _PENDING -> Error
459 * _EXECUTING -> Error
460 * _STOPPING -> Error
461 */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700462 private void handleStartedH(boolean workOngoing) {
463 switch (mVerb) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700464 case VERB_STARTING:
Matthew Williams9b9244b62014-05-14 11:06:04 -0700465 mVerb = VERB_EXECUTING;
Matthew Williams691e93e2014-05-12 15:33:09 -0700466 if (!workOngoing) {
Christopher Tate7060b042014-06-09 19:50:00 -0700467 // Job is finished already so fast-forward to handleFinished.
Matthew Williams9b9244b62014-05-14 11:06:04 -0700468 handleFinishedH(false);
Matthew Williams691e93e2014-05-12 15:33:09 -0700469 return;
Matthew Williams691e93e2014-05-12 15:33:09 -0700470 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700471 if (mCancelled.get()) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700472 if (DEBUG) {
473 Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
474 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700475 // Cancelled *while* waiting for acknowledgeStartMessage from client.
476 handleCancelH();
477 return;
478 }
479 scheduleOpTimeOut();
Matthew Williams691e93e2014-05-12 15:33:09 -0700480 break;
481 default:
Matthew Williamsee410da2014-07-25 11:30:40 -0700482 Slog.e(TAG, "Handling started job but job wasn't starting! Was "
Matthew Williams9b9244b62014-05-14 11:06:04 -0700483 + VERB_STRINGS[mVerb] + ".");
Matthew Williams691e93e2014-05-12 15:33:09 -0700484 return;
485 }
486 }
487
488 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700489 * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
Matthew Williams691e93e2014-05-12 15:33:09 -0700490 * _STOPPING -> Successful finish, clean up and notify done.
491 * _STARTING -> Error
492 * _PENDING -> Error
493 */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700494 private void handleFinishedH(boolean reschedule) {
495 switch (mVerb) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700496 case VERB_EXECUTING:
497 case VERB_STOPPING:
Christopher Tate7060b042014-06-09 19:50:00 -0700498 closeAndCleanupJobH(reschedule);
Matthew Williams691e93e2014-05-12 15:33:09 -0700499 break;
500 default:
Christopher Tate7060b042014-06-09 19:50:00 -0700501 Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
Matthew Williams9b9244b62014-05-14 11:06:04 -0700502 "executed. Was " + VERB_STRINGS[mVerb] + ".");
Matthew Williams691e93e2014-05-12 15:33:09 -0700503 }
504 }
505
506 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700507 * A job can be in various states when a cancel request comes in:
Matthew Williams9b9244b62014-05-14 11:06:04 -0700508 * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
509 * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
510 * _STARTING -> Mark as cancelled and wait for
Christopher Tate7060b042014-06-09 19:50:00 -0700511 * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
Matthew Williamsee410da2014-07-25 11:30:40 -0700512 * _EXECUTING -> call {@link #sendStopMessageH}}, but only if there are no callbacks
513 * in the message queue.
Matthew Williams691e93e2014-05-12 15:33:09 -0700514 * _ENDING -> No point in doing anything here, so we ignore.
515 */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700516 private void handleCancelH() {
Matthew Williamsee410da2014-07-25 11:30:40 -0700517 if (JobSchedulerService.DEBUG) {
518 Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
519 + VERB_STRINGS[mVerb]);
520 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700521 switch (mVerb) {
522 case VERB_BINDING:
Matthew Williams691e93e2014-05-12 15:33:09 -0700523 case VERB_STARTING:
Matthew Williams9b9244b62014-05-14 11:06:04 -0700524 mCancelled.set(true);
Matthew Williams691e93e2014-05-12 15:33:09 -0700525 break;
526 case VERB_EXECUTING:
Matthew Williamsee410da2014-07-25 11:30:40 -0700527 if (hasMessages(MSG_CALLBACK)) {
528 // If the client has called jobFinished, ignore this cancel.
529 return;
530 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700531 sendStopMessageH();
Matthew Williams691e93e2014-05-12 15:33:09 -0700532 break;
533 case VERB_STOPPING:
534 // Nada.
535 break;
536 default:
Christopher Tate7060b042014-06-09 19:50:00 -0700537 Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
Matthew Williams691e93e2014-05-12 15:33:09 -0700538 break;
539 }
540 }
541
Matthew Williams9b9244b62014-05-14 11:06:04 -0700542 /** Process MSG_TIMEOUT here. */
543 private void handleOpTimeoutH() {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700544 switch (mVerb) {
Matthew Williams75fc5252014-09-02 16:17:53 -0700545 case VERB_BINDING:
546 Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
547 ", dropping.");
548 closeAndCleanupJobH(false /* needsReschedule */);
549 break;
Matthew Williams691e93e2014-05-12 15:33:09 -0700550 case VERB_STARTING:
551 // Client unresponsive - wedged or failed to respond in time. We don't really
Christopher Tate7060b042014-06-09 19:50:00 -0700552 // know what happened so let's log it and notify the JobScheduler
Matthew Williams691e93e2014-05-12 15:33:09 -0700553 // FINISHED/NO-RETRY.
Matthew Williamsee410da2014-07-25 11:30:40 -0700554 Slog.e(TAG, "No response from client for onStartJob '" +
Matthew Williams75fc5252014-09-02 16:17:53 -0700555 mRunningJob.toShortString());
Christopher Tate7060b042014-06-09 19:50:00 -0700556 closeAndCleanupJobH(false /* needsReschedule */);
Matthew Williams691e93e2014-05-12 15:33:09 -0700557 break;
558 case VERB_STOPPING:
Christopher Tate7060b042014-06-09 19:50:00 -0700559 // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
Matthew Williamsee410da2014-07-25 11:30:40 -0700560 Slog.e(TAG, "No response from client for onStopJob, '" +
Matthew Williams75fc5252014-09-02 16:17:53 -0700561 mRunningJob.toShortString());
Christopher Tate7060b042014-06-09 19:50:00 -0700562 closeAndCleanupJobH(true /* needsReschedule */);
Matthew Williams691e93e2014-05-12 15:33:09 -0700563 break;
564 case VERB_EXECUTING:
565 // Not an error - client ran out of time.
Matthew Williamsee410da2014-07-25 11:30:40 -0700566 Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
Matthew Williams75fc5252014-09-02 16:17:53 -0700567 " sending onStop. " + mRunningJob.toShortString());
Shreyas Basarge7d089052016-02-22 22:29:39 +0000568 mParams.setStopReason(JobParameters.REASON_TIMEOUT);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700569 sendStopMessageH();
Matthew Williams691e93e2014-05-12 15:33:09 -0700570 break;
571 default:
Matthew Williams75fc5252014-09-02 16:17:53 -0700572 Slog.e(TAG, "Handling timeout for an invalid job state: " +
573 mRunningJob.toShortString() + ", dropping.");
574 closeAndCleanupJobH(false /* needsReschedule */);
Matthew Williams691e93e2014-05-12 15:33:09 -0700575 }
576 }
577
578 /**
Matthew Williams9b9244b62014-05-14 11:06:04 -0700579 * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
580 * VERB_STOPPING.
Matthew Williams691e93e2014-05-12 15:33:09 -0700581 */
Matthew Williams9b9244b62014-05-14 11:06:04 -0700582 private void sendStopMessageH() {
Matthew Williams7ac52d52014-09-12 14:40:18 -0700583 removeOpTimeOut();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700584 if (mVerb != VERB_EXECUTING) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700585 Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
Christopher Tate7060b042014-06-09 19:50:00 -0700586 closeAndCleanupJobH(false /* reschedule */);
Matthew Williams691e93e2014-05-12 15:33:09 -0700587 return;
588 }
589 try {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700590 mVerb = VERB_STOPPING;
591 scheduleOpTimeOut();
Christopher Tate7060b042014-06-09 19:50:00 -0700592 service.stopJob(mParams);
Matthew Williams691e93e2014-05-12 15:33:09 -0700593 } catch (RemoteException e) {
Matthew Williamsee410da2014-07-25 11:30:40 -0700594 Slog.e(TAG, "Error sending onStopJob to client.", e);
Christopher Tate7060b042014-06-09 19:50:00 -0700595 closeAndCleanupJobH(false /* reschedule */);
Matthew Williams691e93e2014-05-12 15:33:09 -0700596 }
597 }
598
599 /**
Christopher Tate7060b042014-06-09 19:50:00 -0700600 * The provided job has finished, either by calling
601 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
Matthew Williams691e93e2014-05-12 15:33:09 -0700602 * or from acknowledging the stop message we sent. Either way, we're done tracking it and
603 * we want to clean up internally.
604 */
Christopher Tate7060b042014-06-09 19:50:00 -0700605 private void closeAndCleanupJobH(boolean reschedule) {
riddle_hsu6a94aba2015-05-01 01:52:58 +0800606 final JobStatus completedJob;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700607 synchronized (mLock) {
riddle_hsu6a94aba2015-05-01 01:52:58 +0800608 if (mVerb == VERB_FINISHED) {
609 return;
610 }
611 completedJob = mRunningJob;
Dianne Hackborn807de782016-04-07 17:54:41 -0700612 mJobPackageTracker.noteInactive(completedJob);
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700613 try {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800614 mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
615 mRunningJob.getSourceUid());
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700616 } catch (RemoteException e) {
617 // Whatever.
618 }
Matthew Williams75fc5252014-09-02 16:17:53 -0700619 if (mWakeLock != null) {
620 mWakeLock.release();
621 }
Christopher Tate7060b042014-06-09 19:50:00 -0700622 mContext.unbindService(JobServiceContext.this);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700623 mWakeLock = null;
Christopher Tate7060b042014-06-09 19:50:00 -0700624 mRunningJob = null;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700625 mParams = null;
riddle_hsu6a94aba2015-05-01 01:52:58 +0800626 mVerb = VERB_FINISHED;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700627 mCancelled.set(false);
628 service = null;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700629 mAvailable = true;
Matthew Williams691e93e2014-05-12 15:33:09 -0700630 }
Matthew Williams7ac52d52014-09-12 14:40:18 -0700631 removeOpTimeOut();
Matthew Williamsee410da2014-07-25 11:30:40 -0700632 removeMessages(MSG_CALLBACK);
633 removeMessages(MSG_SERVICE_BOUND);
634 removeMessages(MSG_CANCEL);
635 removeMessages(MSG_SHUTDOWN_EXECUTION);
636 mCompletedListener.onJobCompleted(completedJob, reschedule);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700637 }
Matthew Williams75fc5252014-09-02 16:17:53 -0700638 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700639
Matthew Williams75fc5252014-09-02 16:17:53 -0700640 /**
641 * Called when sending a message to the client, over whose execution we have no control. If
642 * we haven't received a response in a certain amount of time, we want to give up and carry
643 * on with life.
644 */
645 private void scheduleOpTimeOut() {
Matthew Williams7ac52d52014-09-12 14:40:18 -0700646 removeOpTimeOut();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700647
Matthew Williams75fc5252014-09-02 16:17:53 -0700648 final long timeoutMillis = (mVerb == VERB_EXECUTING) ?
649 EXECUTING_TIMESLICE_MILLIS : OP_TIMEOUT_MILLIS;
650 if (DEBUG) {
651 Slog.d(TAG, "Scheduling time out for '" +
652 mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
653 mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
Matthew Williams691e93e2014-05-12 15:33:09 -0700654 }
Matthew Williams75fc5252014-09-02 16:17:53 -0700655 Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT);
656 mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
657 mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
Matthew Williams6de79e22014-05-01 10:47:00 -0700658 }
Matthew Williams7ac52d52014-09-12 14:40:18 -0700659
660
661 private void removeOpTimeOut() {
662 mCallbackHandler.removeMessages(MSG_TIMEOUT);
663 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700664}