blob: 83a3c1993d4b6d35a9a9e059cec18ac51c3e2a92 [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
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070019import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
20
Matthew Williams691e93e2014-05-12 15:33:09 -070021import android.app.ActivityManager;
Christopher Tate7060b042014-06-09 19:50:00 -070022import android.app.job.IJobCallback;
23import android.app.job.IJobService;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -070024import android.app.job.JobInfo;
25import android.app.job.JobParameters;
Dianne Hackborn7da13d72017-04-04 17:17:35 -070026import android.app.job.JobWorkItem;
Matthew Williams6de79e22014-05-01 10:47:00 -070027import android.content.ComponentName;
Matthew Williams691e93e2014-05-12 15:33:09 -070028import android.content.Context;
29import android.content.Intent;
Matthew Williams6de79e22014-05-01 10:47:00 -070030import android.content.ServiceConnection;
Dianne Hackborn1a30bd92016-01-11 11:05:00 -080031import android.net.Uri;
Matthew Williams9b9244b62014-05-14 11:06:04 -070032import android.os.Binder;
Matthew Williams691e93e2014-05-12 15:33:09 -070033import android.os.Handler;
Matthew Williams6de79e22014-05-01 10:47:00 -070034import android.os.IBinder;
Matthew Williams691e93e2014-05-12 15:33:09 -070035import android.os.Looper;
36import android.os.Message;
37import android.os.PowerManager;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.os.WorkSource;
Christopher Tate373ea902018-01-04 16:10:26 -080041import android.util.EventLog;
Matthew Williams691e93e2014-05-12 15:33:09 -070042import android.util.Slog;
Dianne Hackborn729a3282017-06-09 16:06:01 -070043import android.util.TimeUtils;
Matthew Williams6de79e22014-05-01 10:47:00 -070044
Matthew Williams9b9244b62014-05-14 11:06:04 -070045import com.android.internal.annotations.GuardedBy;
46import com.android.internal.annotations.VisibleForTesting;
Dianne Hackbornfdb19562014-07-11 16:03:36 -070047import com.android.internal.app.IBatteryStats;
Christopher Tate373ea902018-01-04 16:10:26 -080048import com.android.server.EventLogTags;
Christopher Tate7060b042014-06-09 19:50:00 -070049import com.android.server.job.controllers.JobStatus;
Matthew Williams6de79e22014-05-01 10:47:00 -070050
51/**
Matthew Williamsee410da2014-07-25 11:30:40 -070052 * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
53 * class.
54 *
55 * There are two important interactions into this class from the
56 * {@link com.android.server.job.JobSchedulerService}. To execute a job and to cancel a job.
57 * - Execution of a new job is handled by the {@link #mAvailable}. This bit is flipped once when a
58 * job lands, and again when it is complete.
59 * - Cancelling is trickier, because there are also interactions from the client. It's possible
60 * the {@link com.android.server.job.JobServiceContext.JobServiceHandler} tries to process a
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070061 * {@link #doCancelLocked} after the client has already finished. This is handled by having
Dianne Hackborn342e6032017-04-13 18:04:31 -070062 * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelLocked} check whether
Matthew Williamsee410da2014-07-25 11:30:40 -070063 * the context is still valid.
Dianne Hackborn342e6032017-04-13 18:04:31 -070064 * To mitigate this, we avoid sending duplicate onStopJob()
Matthew Williamsee410da2014-07-25 11:30:40 -070065 * calls to the client after they've specified jobFinished().
Matthew Williams6de79e22014-05-01 10:47:00 -070066 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -070067public final class JobServiceContext implements ServiceConnection {
Matthew Williamsfa8e5082015-10-15 15:59:12 -070068 private static final boolean DEBUG = JobSchedulerService.DEBUG;
Christopher Tatea732f012017-10-26 17:26:53 -070069 private static final boolean DEBUG_STANDBY = JobSchedulerService.DEBUG_STANDBY;
70
Christopher Tate7060b042014-06-09 19:50:00 -070071 private static final String TAG = "JobServiceContext";
Christopher Tate7060b042014-06-09 19:50:00 -070072 /** Amount of time a job is allowed to execute for before being considered timed-out. */
Jeff Sharkeycaa3f8d2017-10-25 16:12:00 -060073 public static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
Christopher Tate2022bad2017-02-02 18:07:42 -080074 /** Amount of time the JobScheduler waits for the initial service launch+bind. */
75 private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
Christopher Tate7060b042014-06-09 19:50:00 -070076 /** Amount of time the JobScheduler will wait for a response from an app for a message. */
Matthew Williams691e93e2014-05-12 15:33:09 -070077 private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
Matthew Williams6de79e22014-05-01 10:47:00 -070078
Matthew Williams691e93e2014-05-12 15:33:09 -070079 private static final String[] VERB_STRINGS = {
riddle_hsu6a94aba2015-05-01 01:52:58 +080080 "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
Matthew Williams691e93e2014-05-12 15:33:09 -070081 };
82
Christopher Tate7060b042014-06-09 19:50:00 -070083 // States that a job occupies while interacting with the client.
Matthew Williams9b9244b62014-05-14 11:06:04 -070084 static final int VERB_BINDING = 0;
85 static final int VERB_STARTING = 1;
86 static final int VERB_EXECUTING = 2;
87 static final int VERB_STOPPING = 3;
riddle_hsu6a94aba2015-05-01 01:52:58 +080088 static final int VERB_FINISHED = 4;
Matthew Williams691e93e2014-05-12 15:33:09 -070089
90 // Messages that result from interactions with the client service.
91 /** System timed out waiting for a response. */
92 private static final int MSG_TIMEOUT = 0;
Matthew Williams691e93e2014-05-12 15:33:09 -070093
Shreyas Basarge5db09082016-01-07 13:38:29 +000094 public static final int NO_PREFERRED_UID = -1;
95
Matthew Williams691e93e2014-05-12 15:33:09 -070096 private final Handler mCallbackHandler;
Christopher Tate7060b042014-06-09 19:50:00 -070097 /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
98 private final JobCompletedListener mCompletedListener;
Matthew Williams691e93e2014-05-12 15:33:09 -070099 /** Used for service binding, etc. */
100 private final Context mContext;
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800101 private final Object mLock;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700102 private final IBatteryStats mBatteryStats;
Dianne Hackborn807de782016-04-07 17:54:41 -0700103 private final JobPackageTracker mJobPackageTracker;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700104 private PowerManager.WakeLock mWakeLock;
Matthew Williams6de79e22014-05-01 10:47:00 -0700105
Matthew Williams9b9244b62014-05-14 11:06:04 -0700106 // Execution state.
Christopher Tate7060b042014-06-09 19:50:00 -0700107 private JobParameters mParams;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700108 @VisibleForTesting
109 int mVerb;
Dianne Hackborn342e6032017-04-13 18:04:31 -0700110 private boolean mCancelled;
Matthew Williams6de79e22014-05-01 10:47:00 -0700111
Matthew Williams0cc76542015-10-16 21:04:51 -0700112 /**
113 * All the information maintained about the job currently being executed.
114 *
115 * Any reads (dereferences) not done from the handler thread must be synchronized on
116 * {@link #mLock}.
117 * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
118 */
Christopher Tate7060b042014-06-09 19:50:00 -0700119 private JobStatus mRunningJob;
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700120 private JobCallback mRunningCallback;
Shreyas Basarge5db09082016-01-07 13:38:29 +0000121 /** Used to store next job to run when current job is to be preempted. */
122 private int mPreferredUid;
Christopher Tate7060b042014-06-09 19:50:00 -0700123 IJobService service;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700124
Matthew Williamsee410da2014-07-25 11:30:40 -0700125 /**
126 * Whether this context is free. This is set to false at the start of execution, and reset to
127 * true when execution is complete.
128 */
Matthew Williamseffacfa2014-06-05 20:56:40 -0700129 @GuardedBy("mLock")
Matthew Williams9b9244b62014-05-14 11:06:04 -0700130 private boolean mAvailable;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700131 /** Track start time. */
132 private long mExecutionStartTimeElapsed;
133 /** Track when job will timeout. */
134 private long mTimeoutElapsed;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700135
Dianne Hackborn729a3282017-06-09 16:06:01 -0700136 // Debugging: reason this job was last stopped.
137 public String mStoppedReason;
138
139 // Debugging: time this job was last stopped.
140 public long mStoppedTime;
141
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700142 final class JobCallback extends IJobCallback.Stub {
143 public String mStoppedReason;
144 public long mStoppedTime;
145
146 @Override
147 public void acknowledgeStartMessage(int jobId, boolean ongoing) {
148 doAcknowledgeStartMessage(this, jobId, ongoing);
149 }
150
151 @Override
152 public void acknowledgeStopMessage(int jobId, boolean reschedule) {
153 doAcknowledgeStopMessage(this, jobId, reschedule);
154 }
155
156 @Override
157 public JobWorkItem dequeueWork(int jobId) {
158 return doDequeueWork(this, jobId);
159 }
160
161 @Override
162 public boolean completeWork(int jobId, int workId) {
163 return doCompleteWork(this, jobId, workId);
164 }
165
166 @Override
167 public void jobFinished(int jobId, boolean reschedule) {
168 doJobFinished(this, jobId, reschedule);
169 }
170 }
171
Dianne Hackborn807de782016-04-07 17:54:41 -0700172 JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
173 JobPackageTracker tracker, Looper looper) {
174 this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700175 }
176
177 @VisibleForTesting
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800178 JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
Dianne Hackborn807de782016-04-07 17:54:41 -0700179 JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700180 mContext = context;
Dianne Hackbornd506b2b2016-02-16 10:30:33 -0800181 mLock = lock;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700182 mBatteryStats = batteryStats;
Dianne Hackborn807de782016-04-07 17:54:41 -0700183 mJobPackageTracker = tracker;
Christopher Tate7060b042014-06-09 19:50:00 -0700184 mCallbackHandler = new JobServiceHandler(looper);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700185 mCompletedListener = completedListener;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700186 mAvailable = true;
Shreyas Basarge5db09082016-01-07 13:38:29 +0000187 mVerb = VERB_FINISHED;
188 mPreferredUid = NO_PREFERRED_UID;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700189 }
190
191 /**
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700192 * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()}
Shreyas Basarge5db09082016-01-07 13:38:29 +0000193 * and ensure it is null to make sure this is a valid context.
Christopher Tate7060b042014-06-09 19:50:00 -0700194 * @param job The status of the job that we are going to run.
195 * @return True if the job is valid and is running. False if the job cannot be executed.
Matthew Williams9b9244b62014-05-14 11:06:04 -0700196 */
Christopher Tate7060b042014-06-09 19:50:00 -0700197 boolean executeRunnableJob(JobStatus job) {
Matthew Williamseffacfa2014-06-05 20:56:40 -0700198 synchronized (mLock) {
Matthew Williams9b9244b62014-05-14 11:06:04 -0700199 if (!mAvailable) {
200 Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
201 return false;
202 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700203
Shreyas Basarge5db09082016-01-07 13:38:29 +0000204 mPreferredUid = NO_PREFERRED_UID;
205
Christopher Tate7060b042014-06-09 19:50:00 -0700206 mRunningJob = job;
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700207 mRunningCallback = new JobCallback();
Matthew Williams673ef9e2015-04-03 21:52:02 -0700208 final boolean isDeadlineExpired =
Matthew Williamsd9db9d72015-05-14 17:47:33 -0700209 job.hasDeadlineConstraint() &&
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700210 (job.getLatestRunTimeElapsed() < sElapsedRealtimeClock.millis());
Dianne Hackborn1a30bd92016-01-11 11:05:00 -0800211 Uri[] triggeredUris = null;
212 if (job.changedUris != null) {
213 triggeredUris = new Uri[job.changedUris.size()];
214 job.changedUris.toArray(triggeredUris);
215 }
216 String[] triggeredAuthorities = null;
217 if (job.changedAuthorities != null) {
218 triggeredAuthorities = new String[job.changedAuthorities.size()];
219 job.changedAuthorities.toArray(triggeredAuthorities);
220 }
Dianne Hackborna47223f2017-03-30 13:49:13 -0700221 final JobInfo ji = job.getJob();
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700222 mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
Dianne Hackborna47223f2017-03-30 13:49:13 -0700223 ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
Jeff Sharkey76a02412017-10-24 16:55:04 -0600224 isDeadlineExpired, triggeredUris, triggeredAuthorities, job.network);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700225 mExecutionStartTimeElapsed = sElapsedRealtimeClock.millis();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700226
Christopher Tate373ea902018-01-04 16:10:26 -0800227 final long whenDeferred = job.getWhenStandbyDeferred();
228 if (whenDeferred > 0) {
229 final long deferral = mExecutionStartTimeElapsed - whenDeferred;
230 EventLog.writeEvent(EventLogTags.JOB_DEFERRED_EXECUTION, deferral);
231 if (DEBUG_STANDBY) {
Christopher Tatea732f012017-10-26 17:26:53 -0700232 StringBuilder sb = new StringBuilder(128);
233 sb.append("Starting job deferred for standby by ");
Christopher Tate373ea902018-01-04 16:10:26 -0800234 TimeUtils.formatDuration(deferral, sb);
235 sb.append(" ms : ");
Christopher Tatea732f012017-10-26 17:26:53 -0700236 sb.append(job.toShortString());
237 Slog.v(TAG, sb.toString());
238 }
239 }
Christopher Tate373ea902018-01-04 16:10:26 -0800240
Christopher Tate616541d2017-07-26 14:27:38 -0700241 // Once we'e begun executing a job, we by definition no longer care whether
242 // it was inflated from disk with not-yet-coherent delay/deadline bounds.
243 job.clearPersistedUtcTimes();
244
Matthew Williamseffacfa2014-06-05 20:56:40 -0700245 mVerb = VERB_BINDING;
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700246 scheduleOpTimeOutLocked();
Christopher Tate7060b042014-06-09 19:50:00 -0700247 final Intent intent = new Intent().setComponent(job.getServiceComponent());
Matthew Williamseffacfa2014-06-05 20:56:40 -0700248 boolean binding = mContext.bindServiceAsUser(intent, this,
Dianne Hackbornd1f3df42017-05-01 14:33:05 -0700249 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
Christopher Tate7060b042014-06-09 19:50:00 -0700250 new UserHandle(job.getUserId()));
Matthew Williamseffacfa2014-06-05 20:56:40 -0700251 if (!binding) {
252 if (DEBUG) {
Christopher Tate7060b042014-06-09 19:50:00 -0700253 Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
Matthew Williamseffacfa2014-06-05 20:56:40 -0700254 }
Christopher Tate7060b042014-06-09 19:50:00 -0700255 mRunningJob = null;
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700256 mRunningCallback = null;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700257 mParams = null;
258 mExecutionStartTimeElapsed = 0L;
riddle_hsu6a94aba2015-05-01 01:52:58 +0800259 mVerb = VERB_FINISHED;
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700260 removeOpTimeOutLocked();
Matthew Williamseffacfa2014-06-05 20:56:40 -0700261 return false;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700262 }
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700263 try {
Dianne Hackborn1085ff62016-02-23 17:04:58 -0800264 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700265 } catch (RemoteException e) {
266 // Whatever.
267 }
Dianne Hackborn807de782016-04-07 17:54:41 -0700268 mJobPackageTracker.noteActive(job);
Matthew Williamseffacfa2014-06-05 20:56:40 -0700269 mAvailable = false;
Dianne Hackborn729a3282017-06-09 16:06:01 -0700270 mStoppedReason = null;
271 mStoppedTime = 0;
Matthew Williamseffacfa2014-06-05 20:56:40 -0700272 return true;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700273 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700274 }
275
Matthew Williamsee410da2014-07-25 11:30:40 -0700276 /**
277 * Used externally to query the running job. Will return null if there is no job running.
Matthew Williamsee410da2014-07-25 11:30:40 -0700278 */
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700279 JobStatus getRunningJobLocked() {
Christopher Tateeafb5352016-10-04 16:34:48 -0700280 return mRunningJob;
281 }
282
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700283 /**
284 * Used only for debugging. Will return <code>"&lt;null&gt;"</code> if there is no job running.
285 */
286 private String getRunningJobNameLocked() {
287 return mRunningJob != null ? mRunningJob.toShortString() : "<null>";
288 }
289
Christopher Tate7060b042014-06-09 19:50:00 -0700290 /** Called externally when a job that was scheduled for execution should be cancelled. */
Dianne Hackborn729a3282017-06-09 16:06:01 -0700291 void cancelExecutingJobLocked(int reason, String debugReason) {
292 doCancelLocked(reason, debugReason);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700293 }
294
Dianne Hackborn342e6032017-04-13 18:04:31 -0700295 void preemptExecutingJobLocked() {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700296 doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption");
Shreyas Basarge5db09082016-01-07 13:38:29 +0000297 }
298
299 int getPreferredUid() {
300 return mPreferredUid;
301 }
302
303 void clearPreferredUid() {
304 mPreferredUid = NO_PREFERRED_UID;
Matthew Williams6de79e22014-05-01 10:47:00 -0700305 }
306
Matthew Williamseffacfa2014-06-05 20:56:40 -0700307 long getExecutionStartTimeElapsed() {
308 return mExecutionStartTimeElapsed;
309 }
310
311 long getTimeoutElapsed() {
312 return mTimeoutElapsed;
313 }
314
Makoto Onukid2bfec62018-01-12 13:58:01 -0800315 boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId,
316 String reason) {
Dianne Hackbornbfc23312017-05-11 11:53:24 -0700317 final JobStatus executing = getRunningJobLocked();
Dianne Hackborn83b40f62017-04-26 13:59:47 -0700318 if (executing != null && (userId == UserHandle.USER_ALL || userId == executing.getUserId())
319 && (pkgName == null || pkgName.equals(executing.getSourcePackageName()))
320 && (!matchJobId || jobId == executing.getJobId())) {
321 if (mVerb == VERB_EXECUTING) {
Makoto Onukid2bfec62018-01-12 13:58:01 -0800322 mParams.setStopReason(JobParameters.REASON_TIMEOUT, reason);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700323 sendStopMessageLocked("force timeout from shell");
Dianne Hackborn83b40f62017-04-26 13:59:47 -0700324 return true;
325 }
326 }
327 return false;
328 }
329
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700330 void doJobFinished(JobCallback cb, int jobId, boolean reschedule) {
331 doCallback(cb, reschedule, "app called jobFinished");
Matthew Williams6de79e22014-05-01 10:47:00 -0700332 }
333
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700334 void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
335 doCallback(cb, reschedule, null);
Matthew Williams6de79e22014-05-01 10:47:00 -0700336 }
337
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700338 void doAcknowledgeStartMessage(JobCallback cb, int jobId, boolean ongoing) {
339 doCallback(cb, ongoing, "finished start");
Matthew Williams691e93e2014-05-12 15:33:09 -0700340 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700341
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700342 JobWorkItem doDequeueWork(JobCallback cb, int jobId) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700343 final long ident = Binder.clearCallingIdentity();
344 try {
345 synchronized (mLock) {
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700346 assertCallerLocked(cb);
347 if (mVerb == VERB_STOPPING || mVerb == VERB_FINISHED) {
348 // This job is either all done, or on its way out. Either way, it
349 // should not dispatch any more work. We will pick up any remaining
350 // work the next time we start the job again.
351 return null;
352 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700353 final JobWorkItem work = mRunningJob.dequeueWorkLocked();
354 if (work == null && !mRunningJob.hasExecutingWorkLocked()) {
355 // This will finish the job.
Dianne Hackborn729a3282017-06-09 16:06:01 -0700356 doCallbackLocked(false, "last work dequeued");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700357 }
358 return work;
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700359 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700360 } finally {
361 Binder.restoreCallingIdentity(ident);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700362 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700363 }
364
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700365 boolean doCompleteWork(JobCallback cb, int jobId, int workId) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700366 final long ident = Binder.clearCallingIdentity();
367 try {
368 synchronized (mLock) {
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700369 assertCallerLocked(cb);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700370 return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700371 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700372 } finally {
373 Binder.restoreCallingIdentity(ident);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700374 }
375 }
376
Matthew Williams691e93e2014-05-12 15:33:09 -0700377 /**
Matthew Williams9b9244b62014-05-14 11:06:04 -0700378 * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
Matthew Williams691e93e2014-05-12 15:33:09 -0700379 * we intend to send to the client - we stop sending work when the service is unbound so until
380 * then we keep the wakelock.
Matthew Williams9b9244b62014-05-14 11:06:04 -0700381 * @param name The concrete component name of the service that has been connected.
Matthew Williams691e93e2014-05-12 15:33:09 -0700382 * @param service The IBinder of the Service's communication channel,
383 */
Matthew Williams6de79e22014-05-01 10:47:00 -0700384 @Override
385 public void onServiceConnected(ComponentName name, IBinder service) {
Matthew Williams0cc76542015-10-16 21:04:51 -0700386 JobStatus runningJob;
387 synchronized (mLock) {
388 // This isn't strictly necessary b/c the JobServiceHandler is running on the main
389 // looper and at this point we can't get any binder callbacks from the client. Better
390 // safe than sorry.
391 runningJob = mRunningJob;
Dianne Hackborn342e6032017-04-13 18:04:31 -0700392
393 if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700394 closeAndCleanupJobLocked(true /* needsReschedule */,
395 "connected for different component");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700396 return;
397 }
398 this.service = IJobService.Stub.asInterface(service);
399 final PowerManager pm =
400 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
401 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
402 runningJob.getTag());
403 wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
404 wl.setReferenceCounted(false);
405 wl.acquire();
406
Christopher Tate4b17e982016-09-26 12:59:10 -0700407 // We use a new wakelock instance per job. In rare cases there is a race between
408 // teardown following job completion/cancellation and new job service spin-up
409 // such that if we simply assign mWakeLock to be the new instance, we orphan
410 // the currently-live lock instead of cleanly replacing it. Watch for this and
411 // explicitly fast-forward the release if we're in that situation.
412 if (mWakeLock != null) {
413 Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
414 + " tag=" + mWakeLock.getTag());
415 mWakeLock.release();
416 }
417 mWakeLock = wl;
Dianne Hackborn342e6032017-04-13 18:04:31 -0700418 doServiceBoundLocked();
Christopher Tate4b17e982016-09-26 12:59:10 -0700419 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700420 }
421
Matthew Williamsee410da2014-07-25 11:30:40 -0700422 /** If the client service crashes we reschedule this job and clean up. */
Matthew Williams6de79e22014-05-01 10:47:00 -0700423 @Override
424 public void onServiceDisconnected(ComponentName name) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700425 synchronized (mLock) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700426 closeAndCleanupJobLocked(true /* needsReschedule */, "unexpectedly disconnected");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700427 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700428 }
429
430 /**
Matthew Williams9b9244b62014-05-14 11:06:04 -0700431 * This class is reused across different clients, and passes itself in as a callback. Check
432 * whether the client exercising the callback is the client we expect.
433 * @return True if the binder calling is coming from the client we expect.
Matthew Williams691e93e2014-05-12 15:33:09 -0700434 */
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700435 private boolean verifyCallerLocked(JobCallback cb) {
436 if (mRunningCallback != cb) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700437 if (DEBUG) {
438 Slog.d(TAG, "Stale callback received, ignoring.");
Matthew Williams9b9244b62014-05-14 11:06:04 -0700439 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700440 return false;
Matthew Williams691e93e2014-05-12 15:33:09 -0700441 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700442 return true;
Matthew Williams691e93e2014-05-12 15:33:09 -0700443 }
444
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700445 private void assertCallerLocked(JobCallback cb) {
446 if (!verifyCallerLocked(cb)) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700447 StringBuilder sb = new StringBuilder(128);
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700448 sb.append("Caller no longer running");
449 if (cb.mStoppedReason != null) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700450 sb.append(", last stopped ");
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700451 TimeUtils.formatDuration(sElapsedRealtimeClock.millis() - cb.mStoppedTime, sb);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700452 sb.append(" because: ");
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700453 sb.append(cb.mStoppedReason);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700454 }
455 throw new SecurityException(sb.toString());
456 }
457 }
458
Matthew Williams691e93e2014-05-12 15:33:09 -0700459 /**
Dianne Hackborn342e6032017-04-13 18:04:31 -0700460 * Scheduling of async messages (basically timeouts at this point).
Matthew Williams691e93e2014-05-12 15:33:09 -0700461 */
Christopher Tate7060b042014-06-09 19:50:00 -0700462 private class JobServiceHandler extends Handler {
463 JobServiceHandler(Looper looper) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700464 super(looper);
465 }
466
467 @Override
468 public void handleMessage(Message message) {
469 switch (message.what) {
Matthew Williams691e93e2014-05-12 15:33:09 -0700470 case MSG_TIMEOUT:
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700471 synchronized (mLock) {
Dianne Hackborn62292da2017-06-19 14:24:51 -0700472 if (message.obj == mRunningCallback) {
473 handleOpTimeoutLocked();
474 } else {
475 JobCallback jc = (JobCallback)message.obj;
476 StringBuilder sb = new StringBuilder(128);
477 sb.append("Ignoring timeout of no longer active job");
478 if (jc.mStoppedReason != null) {
479 sb.append(", stopped ");
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700480 TimeUtils.formatDuration(sElapsedRealtimeClock.millis()
Dianne Hackborn62292da2017-06-19 14:24:51 -0700481 - jc.mStoppedTime, sb);
482 sb.append(" because: ");
483 sb.append(jc.mStoppedReason);
484 }
485 Slog.w(TAG, sb.toString());
486 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700487 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700488 break;
Matthew Williams691e93e2014-05-12 15:33:09 -0700489 default:
Matthew Williamsee410da2014-07-25 11:30:40 -0700490 Slog.e(TAG, "Unrecognised message: " + message);
Matthew Williams691e93e2014-05-12 15:33:09 -0700491 }
492 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700493 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700494
Dianne Hackborn342e6032017-04-13 18:04:31 -0700495 void doServiceBoundLocked() {
496 removeOpTimeOutLocked();
497 handleServiceBoundLocked();
498 }
499
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700500 void doCallback(JobCallback cb, boolean reschedule, String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700501 final long ident = Binder.clearCallingIdentity();
502 try {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700503 synchronized (mLock) {
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700504 if (!verifyCallerLocked(cb)) {
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700505 return;
506 }
Dianne Hackborn729a3282017-06-09 16:06:01 -0700507 doCallbackLocked(reschedule, reason);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700508 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700509 } finally {
510 Binder.restoreCallingIdentity(ident);
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700511 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700512 }
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700513
Dianne Hackborn729a3282017-06-09 16:06:01 -0700514 void doCallbackLocked(boolean reschedule, String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700515 if (DEBUG) {
516 Slog.d(TAG, "doCallback of : " + mRunningJob
517 + " v:" + VERB_STRINGS[mVerb]);
518 }
519 removeOpTimeOutLocked();
520
521 if (mVerb == VERB_STARTING) {
522 handleStartedLocked(reschedule);
523 } else if (mVerb == VERB_EXECUTING ||
524 mVerb == VERB_STOPPING) {
Dianne Hackborn729a3282017-06-09 16:06:01 -0700525 handleFinishedLocked(reschedule, reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700526 } else {
Matthew Williams75fc5252014-09-02 16:17:53 -0700527 if (DEBUG) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700528 Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
Matthew Williams9b9244b62014-05-14 11:06:04 -0700529 }
530 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700531 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700532
Dianne Hackborn729a3282017-06-09 16:06:01 -0700533 void doCancelLocked(int arg1, String debugReason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700534 if (mVerb == VERB_FINISHED) {
535 if (DEBUG) {
536 Slog.d(TAG,
537 "Trying to process cancel for torn-down context, ignoring.");
Matthew Williams691e93e2014-05-12 15:33:09 -0700538 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700539 return;
Matthew Williams691e93e2014-05-12 15:33:09 -0700540 }
Makoto Onukid2bfec62018-01-12 13:58:01 -0800541 mParams.setStopReason(arg1, debugReason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700542 if (arg1 == JobParameters.REASON_PREEMPT) {
543 mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
544 NO_PREFERRED_UID;
Matthew Williams691e93e2014-05-12 15:33:09 -0700545 }
Dianne Hackborn729a3282017-06-09 16:06:01 -0700546 handleCancelLocked(debugReason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700547 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700548
Dianne Hackborn342e6032017-04-13 18:04:31 -0700549 /** Start the job on the service. */
550 private void handleServiceBoundLocked() {
551 if (DEBUG) {
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700552 Slog.d(TAG, "handleServiceBound for " + getRunningJobNameLocked());
Matthew Williams691e93e2014-05-12 15:33:09 -0700553 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700554 if (mVerb != VERB_BINDING) {
555 Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
556 + VERB_STRINGS[mVerb]);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700557 closeAndCleanupJobLocked(false /* reschedule */, "started job not pending");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700558 return;
Matthew Williams691e93e2014-05-12 15:33:09 -0700559 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700560 if (mCancelled) {
561 if (DEBUG) {
562 Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
563 + mRunningJob);
Matthew Williams691e93e2014-05-12 15:33:09 -0700564 }
Dianne Hackborn729a3282017-06-09 16:06:01 -0700565 closeAndCleanupJobLocked(true /* reschedule */, "cancelled while waiting for bind");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700566 return;
Matthew Williams691e93e2014-05-12 15:33:09 -0700567 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700568 try {
569 mVerb = VERB_STARTING;
570 scheduleOpTimeOutLocked();
571 service.startJob(mParams);
572 } catch (Exception e) {
573 // We catch 'Exception' because client-app malice or bugs might induce a wide
574 // range of possible exception-throw outcomes from startJob() and its handling
575 // of the client's ParcelableBundle extras.
576 Slog.e(TAG, "Error sending onStart message to '" +
577 mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
578 }
579 }
Matthew Williams691e93e2014-05-12 15:33:09 -0700580
Dianne Hackborn342e6032017-04-13 18:04:31 -0700581 /**
582 * State behaviours.
583 * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
584 * _PENDING -> Error
585 * _EXECUTING -> Error
586 * _STOPPING -> Error
587 */
588 private void handleStartedLocked(boolean workOngoing) {
589 switch (mVerb) {
590 case VERB_STARTING:
591 mVerb = VERB_EXECUTING;
592 if (!workOngoing) {
593 // Job is finished already so fast-forward to handleFinished.
Dianne Hackborn729a3282017-06-09 16:06:01 -0700594 handleFinishedLocked(false, "onStartJob returned false");
riddle_hsu6a94aba2015-05-01 01:52:58 +0800595 return;
596 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700597 if (mCancelled) {
598 if (DEBUG) {
599 Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
600 }
601 // Cancelled *while* waiting for acknowledgeStartMessage from client.
Dianne Hackborn729a3282017-06-09 16:06:01 -0700602 handleCancelLocked(null);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700603 return;
Dianne Hackbornfdb19562014-07-11 16:03:36 -0700604 }
Dianne Hackborn342e6032017-04-13 18:04:31 -0700605 scheduleOpTimeOutLocked();
606 break;
607 default:
608 Slog.e(TAG, "Handling started job but job wasn't starting! Was "
609 + VERB_STRINGS[mVerb] + ".");
610 return;
Matthew Williams9b9244b62014-05-14 11:06:04 -0700611 }
Matthew Williams75fc5252014-09-02 16:17:53 -0700612 }
Matthew Williams9b9244b62014-05-14 11:06:04 -0700613
Matthew Williams75fc5252014-09-02 16:17:53 -0700614 /**
Dianne Hackborn342e6032017-04-13 18:04:31 -0700615 * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
616 * _STOPPING -> Successful finish, clean up and notify done.
617 * _STARTING -> Error
618 * _PENDING -> Error
619 */
Dianne Hackborn729a3282017-06-09 16:06:01 -0700620 private void handleFinishedLocked(boolean reschedule, String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700621 switch (mVerb) {
622 case VERB_EXECUTING:
623 case VERB_STOPPING:
Dianne Hackborn729a3282017-06-09 16:06:01 -0700624 closeAndCleanupJobLocked(reschedule, reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700625 break;
626 default:
627 Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
628 "executed. Was " + VERB_STRINGS[mVerb] + ".");
629 }
630 }
631
632 /**
633 * A job can be in various states when a cancel request comes in:
634 * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
635 * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
636 * _STARTING -> Mark as cancelled and wait for
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700637 * {@link JobServiceContext#doAcknowledgeStartMessage}
Dianne Hackborn342e6032017-04-13 18:04:31 -0700638 * _EXECUTING -> call {@link #sendStopMessageLocked}}, but only if there are no callbacks
639 * in the message queue.
640 * _ENDING -> No point in doing anything here, so we ignore.
641 */
Dianne Hackborn729a3282017-06-09 16:06:01 -0700642 private void handleCancelLocked(String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700643 if (JobSchedulerService.DEBUG) {
644 Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
645 + VERB_STRINGS[mVerb]);
646 }
647 switch (mVerb) {
648 case VERB_BINDING:
649 case VERB_STARTING:
650 mCancelled = true;
Dianne Hackborn729a3282017-06-09 16:06:01 -0700651 applyStoppedReasonLocked(reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700652 break;
653 case VERB_EXECUTING:
Dianne Hackborn729a3282017-06-09 16:06:01 -0700654 sendStopMessageLocked(reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700655 break;
656 case VERB_STOPPING:
657 // Nada.
658 break;
659 default:
660 Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
661 break;
662 }
663 }
664
665 /** Process MSG_TIMEOUT here. */
666 private void handleOpTimeoutLocked() {
667 switch (mVerb) {
668 case VERB_BINDING:
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700669 Slog.w(TAG, "Time-out while trying to bind " + getRunningJobNameLocked()
670 + ", dropping.");
Dianne Hackborn729a3282017-06-09 16:06:01 -0700671 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700672 break;
673 case VERB_STARTING:
674 // Client unresponsive - wedged or failed to respond in time. We don't really
675 // know what happened so let's log it and notify the JobScheduler
676 // FINISHED/NO-RETRY.
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700677 Slog.w(TAG, "No response from client for onStartJob "
678 + getRunningJobNameLocked());
Dianne Hackborn729a3282017-06-09 16:06:01 -0700679 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700680 break;
681 case VERB_STOPPING:
682 // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700683 Slog.w(TAG, "No response from client for onStopJob "
684 + getRunningJobNameLocked());
Dianne Hackborn729a3282017-06-09 16:06:01 -0700685 closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700686 break;
687 case VERB_EXECUTING:
688 // Not an error - client ran out of time.
Dianne Hackborn62292da2017-06-19 14:24:51 -0700689 Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700690 "sending onStop: " + getRunningJobNameLocked());
Makoto Onukid2bfec62018-01-12 13:58:01 -0800691 mParams.setStopReason(JobParameters.REASON_TIMEOUT, "client timed out");
Dianne Hackborn729a3282017-06-09 16:06:01 -0700692 sendStopMessageLocked("timeout while executing");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700693 break;
694 default:
Yohei Yukawa98cf0332017-08-31 15:49:15 -0700695 Slog.e(TAG, "Handling timeout for an invalid job state: "
696 + getRunningJobNameLocked() + ", dropping.");
Dianne Hackborn729a3282017-06-09 16:06:01 -0700697 closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700698 }
699 }
700
701 /**
702 * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
703 * VERB_STOPPING.
704 */
Dianne Hackborn729a3282017-06-09 16:06:01 -0700705 private void sendStopMessageLocked(String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700706 removeOpTimeOutLocked();
707 if (mVerb != VERB_EXECUTING) {
708 Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
Dianne Hackborn729a3282017-06-09 16:06:01 -0700709 closeAndCleanupJobLocked(false /* reschedule */, reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700710 return;
711 }
712 try {
Dianne Hackborneb90fa92017-06-30 14:03:36 -0700713 applyStoppedReasonLocked(reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700714 mVerb = VERB_STOPPING;
715 scheduleOpTimeOutLocked();
716 service.stopJob(mParams);
717 } catch (RemoteException e) {
718 Slog.e(TAG, "Error sending onStopJob to client.", e);
719 // The job's host app apparently crashed during the job, so we should reschedule.
Dianne Hackborn729a3282017-06-09 16:06:01 -0700720 closeAndCleanupJobLocked(true /* reschedule */, "host crashed when trying to stop");
Dianne Hackborn342e6032017-04-13 18:04:31 -0700721 }
722 }
723
724 /**
725 * The provided job has finished, either by calling
726 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
727 * or from acknowledging the stop message we sent. Either way, we're done tracking it and
728 * we want to clean up internally.
729 */
Dianne Hackborn729a3282017-06-09 16:06:01 -0700730 private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
Dianne Hackborn342e6032017-04-13 18:04:31 -0700731 final JobStatus completedJob;
732 if (mVerb == VERB_FINISHED) {
733 return;
734 }
Dianne Hackborn729a3282017-06-09 16:06:01 -0700735 applyStoppedReasonLocked(reason);
Dianne Hackborn342e6032017-04-13 18:04:31 -0700736 completedJob = mRunningJob;
Dianne Hackborn94326cb2017-06-28 16:17:20 -0700737 mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason());
Dianne Hackborn342e6032017-04-13 18:04:31 -0700738 try {
739 mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
Dianne Hackborn94326cb2017-06-28 16:17:20 -0700740 mRunningJob.getSourceUid(), mParams.getStopReason());
Dianne Hackborn342e6032017-04-13 18:04:31 -0700741 } catch (RemoteException e) {
742 // Whatever.
743 }
744 if (mWakeLock != null) {
745 mWakeLock.release();
746 }
747 mContext.unbindService(JobServiceContext.this);
748 mWakeLock = null;
749 mRunningJob = null;
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700750 mRunningCallback = null;
Dianne Hackborn342e6032017-04-13 18:04:31 -0700751 mParams = null;
752 mVerb = VERB_FINISHED;
753 mCancelled = false;
754 service = null;
755 mAvailable = true;
756 removeOpTimeOutLocked();
757 mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
758 }
759
Dianne Hackborn729a3282017-06-09 16:06:01 -0700760 private void applyStoppedReasonLocked(String reason) {
761 if (reason != null && mStoppedReason == null) {
762 mStoppedReason = reason;
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700763 mStoppedTime = sElapsedRealtimeClock.millis();
Dianne Hackborn6466c1c2017-06-13 10:33:19 -0700764 if (mRunningCallback != null) {
765 mRunningCallback.mStoppedReason = mStoppedReason;
766 mRunningCallback.mStoppedTime = mStoppedTime;
767 }
Dianne Hackborn729a3282017-06-09 16:06:01 -0700768 }
769 }
770
Dianne Hackborn342e6032017-04-13 18:04:31 -0700771 /**
Matthew Williams75fc5252014-09-02 16:17:53 -0700772 * Called when sending a message to the client, over whose execution we have no control. If
773 * we haven't received a response in a certain amount of time, we want to give up and carry
774 * on with life.
775 */
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700776 private void scheduleOpTimeOutLocked() {
777 removeOpTimeOutLocked();
Matthew Williams9b9244b62014-05-14 11:06:04 -0700778
Christopher Tate2022bad2017-02-02 18:07:42 -0800779 final long timeoutMillis;
780 switch (mVerb) {
781 case VERB_EXECUTING:
782 timeoutMillis = EXECUTING_TIMESLICE_MILLIS;
783 break;
784
785 case VERB_BINDING:
786 timeoutMillis = OP_BIND_TIMEOUT_MILLIS;
787 break;
788
789 default:
790 timeoutMillis = OP_TIMEOUT_MILLIS;
791 break;
792 }
Matthew Williams75fc5252014-09-02 16:17:53 -0700793 if (DEBUG) {
794 Slog.d(TAG, "Scheduling time out for '" +
795 mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
796 mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
Matthew Williams691e93e2014-05-12 15:33:09 -0700797 }
Dianne Hackborn62292da2017-06-19 14:24:51 -0700798 Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
Matthew Williams75fc5252014-09-02 16:17:53 -0700799 mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
Jeff Sharkeyd0fff2e2017-11-07 16:55:06 -0700800 mTimeoutElapsed = sElapsedRealtimeClock.millis() + timeoutMillis;
Matthew Williams6de79e22014-05-01 10:47:00 -0700801 }
Matthew Williams7ac52d52014-09-12 14:40:18 -0700802
803
Dianne Hackborn7da13d72017-04-04 17:17:35 -0700804 private void removeOpTimeOutLocked() {
Matthew Williams7ac52d52014-09-12 14:40:18 -0700805 mCallbackHandler.removeMessages(MSG_TIMEOUT);
806 }
Matthew Williams6de79e22014-05-01 10:47:00 -0700807}