2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.server.job;
19 import android.app.ActivityManager;
20 import android.app.job.JobInfo;
21 import android.app.job.JobParameters;
22 import android.app.job.IJobCallback;
23 import android.app.job.IJobService;
24 import android.app.job.JobWorkItem;
25 import android.content.ComponentName;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.net.Uri;
30 import android.os.Binder;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.Looper;
34 import android.os.Message;
35 import android.os.PowerManager;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.os.WorkSource;
40 import android.util.Slog;
41 import android.util.TimeUtils;
43 import com.android.internal.annotations.GuardedBy;
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.app.IBatteryStats;
46 import com.android.server.job.controllers.JobStatus;
49 * Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
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 #doCancelLocked} after the client has already finished. This is handled by having
59 * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelLocked} check whether
60 * the context is still valid.
61 * To mitigate this, we avoid sending duplicate onStopJob()
62 * calls to the client after they've specified jobFinished().
64 public final class JobServiceContext implements ServiceConnection {
65 private static final boolean DEBUG = JobSchedulerService.DEBUG;
66 private static final String TAG = "JobServiceContext";
67 /** Amount of time a job is allowed to execute for before being considered timed-out. */
68 private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
69 /** Amount of time the JobScheduler waits for the initial service launch+bind. */
70 private static final long OP_BIND_TIMEOUT_MILLIS = 18 * 1000;
71 /** Amount of time the JobScheduler will wait for a response from an app for a message. */
72 private static final long OP_TIMEOUT_MILLIS = 8 * 1000;
74 private static final String[] VERB_STRINGS = {
75 "VERB_BINDING", "VERB_STARTING", "VERB_EXECUTING", "VERB_STOPPING", "VERB_FINISHED"
78 // States that a job occupies while interacting with the client.
79 static final int VERB_BINDING = 0;
80 static final int VERB_STARTING = 1;
81 static final int VERB_EXECUTING = 2;
82 static final int VERB_STOPPING = 3;
83 static final int VERB_FINISHED = 4;
85 // Messages that result from interactions with the client service.
86 /** System timed out waiting for a response. */
87 private static final int MSG_TIMEOUT = 0;
89 public static final int NO_PREFERRED_UID = -1;
91 private final Handler mCallbackHandler;
92 /** Make callbacks to {@link JobSchedulerService} to inform on job completion status. */
93 private final JobCompletedListener mCompletedListener;
94 /** Used for service binding, etc. */
95 private final Context mContext;
96 private final Object mLock;
97 private final IBatteryStats mBatteryStats;
98 private final JobPackageTracker mJobPackageTracker;
99 private PowerManager.WakeLock mWakeLock;
102 private JobParameters mParams;
105 private boolean mCancelled;
108 * All the information maintained about the job currently being executed.
110 * Any reads (dereferences) not done from the handler thread must be synchronized on
112 * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
114 private JobStatus mRunningJob;
115 private JobCallback mRunningCallback;
116 /** Used to store next job to run when current job is to be preempted. */
117 private int mPreferredUid;
121 * Whether this context is free. This is set to false at the start of execution, and reset to
122 * true when execution is complete.
125 private boolean mAvailable;
126 /** Track start time. */
127 private long mExecutionStartTimeElapsed;
128 /** Track when job will timeout. */
129 private long mTimeoutElapsed;
131 // Debugging: reason this job was last stopped.
132 public String mStoppedReason;
134 // Debugging: time this job was last stopped.
135 public long mStoppedTime;
137 final class JobCallback extends IJobCallback.Stub {
138 public String mStoppedReason;
139 public long mStoppedTime;
142 public void acknowledgeStartMessage(int jobId, boolean ongoing) {
143 doAcknowledgeStartMessage(this, jobId, ongoing);
147 public void acknowledgeStopMessage(int jobId, boolean reschedule) {
148 doAcknowledgeStopMessage(this, jobId, reschedule);
152 public JobWorkItem dequeueWork(int jobId) {
153 return doDequeueWork(this, jobId);
157 public boolean completeWork(int jobId, int workId) {
158 return doCompleteWork(this, jobId, workId);
162 public void jobFinished(int jobId, boolean reschedule) {
163 doJobFinished(this, jobId, reschedule);
167 JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
168 JobPackageTracker tracker, Looper looper) {
169 this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
173 JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
174 JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
177 mBatteryStats = batteryStats;
178 mJobPackageTracker = tracker;
179 mCallbackHandler = new JobServiceHandler(looper);
180 mCompletedListener = completedListener;
182 mVerb = VERB_FINISHED;
183 mPreferredUid = NO_PREFERRED_UID;
187 * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()}
188 * and ensure it is null to make sure this is a valid context.
189 * @param job The status of the job that we are going to run.
190 * @return True if the job is valid and is running. False if the job cannot be executed.
192 boolean executeRunnableJob(JobStatus job) {
193 synchronized (mLock) {
195 Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
199 mPreferredUid = NO_PREFERRED_UID;
202 mRunningCallback = new JobCallback();
203 final boolean isDeadlineExpired =
204 job.hasDeadlineConstraint() &&
205 (job.getLatestRunTimeElapsed() < SystemClock.elapsedRealtime());
206 Uri[] triggeredUris = null;
207 if (job.changedUris != null) {
208 triggeredUris = new Uri[job.changedUris.size()];
209 job.changedUris.toArray(triggeredUris);
211 String[] triggeredAuthorities = null;
212 if (job.changedAuthorities != null) {
213 triggeredAuthorities = new String[job.changedAuthorities.size()];
214 job.changedAuthorities.toArray(triggeredAuthorities);
216 final JobInfo ji = job.getJob();
217 mParams = new JobParameters(mRunningCallback, job.getJobId(), ji.getExtras(),
218 ji.getTransientExtras(), ji.getClipData(), ji.getClipGrantFlags(),
219 isDeadlineExpired, triggeredUris, triggeredAuthorities);
220 mExecutionStartTimeElapsed = SystemClock.elapsedRealtime();
222 // Once we'e begun executing a job, we by definition no longer care whether
223 // it was inflated from disk with not-yet-coherent delay/deadline bounds.
224 job.clearPersistedUtcTimes();
226 mVerb = VERB_BINDING;
227 scheduleOpTimeOutLocked();
228 final Intent intent = new Intent().setComponent(job.getServiceComponent());
229 boolean binding = mContext.bindServiceAsUser(intent, this,
230 Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND,
231 new UserHandle(job.getUserId()));
234 Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
237 mRunningCallback = null;
239 mExecutionStartTimeElapsed = 0L;
240 mVerb = VERB_FINISHED;
241 removeOpTimeOutLocked();
245 mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
246 } catch (RemoteException e) {
249 mJobPackageTracker.noteActive(job);
251 mStoppedReason = null;
258 * Used externally to query the running job. Will return null if there is no job running.
260 JobStatus getRunningJobLocked() {
264 /** Called externally when a job that was scheduled for execution should be cancelled. */
265 void cancelExecutingJobLocked(int reason, String debugReason) {
266 doCancelLocked(reason, debugReason);
269 void preemptExecutingJobLocked() {
270 doCancelLocked(JobParameters.REASON_PREEMPT, "cancelled due to preemption");
273 int getPreferredUid() {
274 return mPreferredUid;
277 void clearPreferredUid() {
278 mPreferredUid = NO_PREFERRED_UID;
281 long getExecutionStartTimeElapsed() {
282 return mExecutionStartTimeElapsed;
285 long getTimeoutElapsed() {
286 return mTimeoutElapsed;
289 boolean timeoutIfExecutingLocked(String pkgName, int userId, boolean matchJobId, int jobId) {
290 final JobStatus executing = getRunningJobLocked();
291 if (executing != null && (userId == UserHandle.USER_ALL || userId == executing.getUserId())
292 && (pkgName == null || pkgName.equals(executing.getSourcePackageName()))
293 && (!matchJobId || jobId == executing.getJobId())) {
294 if (mVerb == VERB_EXECUTING) {
295 mParams.setStopReason(JobParameters.REASON_TIMEOUT);
296 sendStopMessageLocked("force timeout from shell");
303 void doJobFinished(JobCallback cb, int jobId, boolean reschedule) {
304 doCallback(cb, reschedule, "app called jobFinished");
307 void doAcknowledgeStopMessage(JobCallback cb, int jobId, boolean reschedule) {
308 doCallback(cb, reschedule, null);
311 void doAcknowledgeStartMessage(JobCallback cb, int jobId, boolean ongoing) {
312 doCallback(cb, ongoing, "finished start");
315 JobWorkItem doDequeueWork(JobCallback cb, int jobId) {
316 final long ident = Binder.clearCallingIdentity();
318 synchronized (mLock) {
319 assertCallerLocked(cb);
320 if (mVerb == VERB_STOPPING || mVerb == VERB_FINISHED) {
321 // This job is either all done, or on its way out. Either way, it
322 // should not dispatch any more work. We will pick up any remaining
323 // work the next time we start the job again.
326 final JobWorkItem work = mRunningJob.dequeueWorkLocked();
327 if (work == null && !mRunningJob.hasExecutingWorkLocked()) {
328 // This will finish the job.
329 doCallbackLocked(false, "last work dequeued");
334 Binder.restoreCallingIdentity(ident);
338 boolean doCompleteWork(JobCallback cb, int jobId, int workId) {
339 final long ident = Binder.clearCallingIdentity();
341 synchronized (mLock) {
342 assertCallerLocked(cb);
343 return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
346 Binder.restoreCallingIdentity(ident);
351 * We acquire/release a wakelock on onServiceConnected/unbindService. This mirrors the work
352 * we intend to send to the client - we stop sending work when the service is unbound so until
353 * then we keep the wakelock.
354 * @param name The concrete component name of the service that has been connected.
355 * @param service The IBinder of the Service's communication channel,
358 public void onServiceConnected(ComponentName name, IBinder service) {
359 JobStatus runningJob;
360 synchronized (mLock) {
361 // This isn't strictly necessary b/c the JobServiceHandler is running on the main
362 // looper and at this point we can't get any binder callbacks from the client. Better
364 runningJob = mRunningJob;
366 if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
367 closeAndCleanupJobLocked(true /* needsReschedule */,
368 "connected for different component");
371 this.service = IJobService.Stub.asInterface(service);
372 final PowerManager pm =
373 (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
374 PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
375 runningJob.getTag());
376 wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
377 wl.setReferenceCounted(false);
380 // We use a new wakelock instance per job. In rare cases there is a race between
381 // teardown following job completion/cancellation and new job service spin-up
382 // such that if we simply assign mWakeLock to be the new instance, we orphan
383 // the currently-live lock instead of cleanly replacing it. Watch for this and
384 // explicitly fast-forward the release if we're in that situation.
385 if (mWakeLock != null) {
386 Slog.w(TAG, "Bound new job " + runningJob + " but live wakelock " + mWakeLock
387 + " tag=" + mWakeLock.getTag());
391 doServiceBoundLocked();
395 /** If the client service crashes we reschedule this job and clean up. */
397 public void onServiceDisconnected(ComponentName name) {
398 synchronized (mLock) {
399 closeAndCleanupJobLocked(true /* needsReschedule */, "unexpectedly disconnected");
404 * This class is reused across different clients, and passes itself in as a callback. Check
405 * whether the client exercising the callback is the client we expect.
406 * @return True if the binder calling is coming from the client we expect.
408 private boolean verifyCallerLocked(JobCallback cb) {
409 if (mRunningCallback != cb) {
411 Slog.d(TAG, "Stale callback received, ignoring.");
418 private void assertCallerLocked(JobCallback cb) {
419 if (!verifyCallerLocked(cb)) {
420 StringBuilder sb = new StringBuilder(128);
421 sb.append("Caller no longer running");
422 if (cb.mStoppedReason != null) {
423 sb.append(", last stopped ");
424 TimeUtils.formatDuration(SystemClock.elapsedRealtime() - cb.mStoppedTime, sb);
425 sb.append(" because: ");
426 sb.append(cb.mStoppedReason);
428 throw new SecurityException(sb.toString());
433 * Scheduling of async messages (basically timeouts at this point).
435 private class JobServiceHandler extends Handler {
436 JobServiceHandler(Looper looper) {
441 public void handleMessage(Message message) {
442 switch (message.what) {
444 synchronized (mLock) {
445 if (message.obj == mRunningCallback) {
446 handleOpTimeoutLocked();
448 JobCallback jc = (JobCallback)message.obj;
449 StringBuilder sb = new StringBuilder(128);
450 sb.append("Ignoring timeout of no longer active job");
451 if (jc.mStoppedReason != null) {
452 sb.append(", stopped ");
453 TimeUtils.formatDuration(SystemClock.elapsedRealtime()
454 - jc.mStoppedTime, sb);
455 sb.append(" because: ");
456 sb.append(jc.mStoppedReason);
458 Slog.w(TAG, sb.toString());
463 Slog.e(TAG, "Unrecognised message: " + message);
468 void doServiceBoundLocked() {
469 removeOpTimeOutLocked();
470 handleServiceBoundLocked();
473 void doCallback(JobCallback cb, boolean reschedule, String reason) {
474 final long ident = Binder.clearCallingIdentity();
476 synchronized (mLock) {
477 if (!verifyCallerLocked(cb)) {
480 doCallbackLocked(reschedule, reason);
483 Binder.restoreCallingIdentity(ident);
487 void doCallbackLocked(boolean reschedule, String reason) {
489 Slog.d(TAG, "doCallback of : " + mRunningJob
490 + " v:" + VERB_STRINGS[mVerb]);
492 removeOpTimeOutLocked();
494 if (mVerb == VERB_STARTING) {
495 handleStartedLocked(reschedule);
496 } else if (mVerb == VERB_EXECUTING ||
497 mVerb == VERB_STOPPING) {
498 handleFinishedLocked(reschedule, reason);
501 Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
506 void doCancelLocked(int arg1, String debugReason) {
507 if (mVerb == VERB_FINISHED) {
510 "Trying to process cancel for torn-down context, ignoring.");
514 mParams.setStopReason(arg1);
515 if (arg1 == JobParameters.REASON_PREEMPT) {
516 mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
519 handleCancelLocked(debugReason);
522 /** Start the job on the service. */
523 private void handleServiceBoundLocked() {
525 Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString());
527 if (mVerb != VERB_BINDING) {
528 Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
529 + VERB_STRINGS[mVerb]);
530 closeAndCleanupJobLocked(false /* reschedule */, "started job not pending");
535 Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
538 closeAndCleanupJobLocked(true /* reschedule */, "cancelled while waiting for bind");
542 mVerb = VERB_STARTING;
543 scheduleOpTimeOutLocked();
544 service.startJob(mParams);
545 } catch (Exception e) {
546 // We catch 'Exception' because client-app malice or bugs might induce a wide
547 // range of possible exception-throw outcomes from startJob() and its handling
548 // of the client's ParcelableBundle extras.
549 Slog.e(TAG, "Error sending onStart message to '" +
550 mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
556 * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
558 * _EXECUTING -> Error
561 private void handleStartedLocked(boolean workOngoing) {
564 mVerb = VERB_EXECUTING;
566 // Job is finished already so fast-forward to handleFinished.
567 handleFinishedLocked(false, "onStartJob returned false");
572 Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
574 // Cancelled *while* waiting for acknowledgeStartMessage from client.
575 handleCancelLocked(null);
578 scheduleOpTimeOutLocked();
581 Slog.e(TAG, "Handling started job but job wasn't starting! Was "
582 + VERB_STRINGS[mVerb] + ".");
588 * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
589 * _STOPPING -> Successful finish, clean up and notify done.
593 private void handleFinishedLocked(boolean reschedule, String reason) {
597 closeAndCleanupJobLocked(reschedule, reason);
600 Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
601 "executed. Was " + VERB_STRINGS[mVerb] + ".");
606 * A job can be in various states when a cancel request comes in:
607 * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
608 * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
609 * _STARTING -> Mark as cancelled and wait for
610 * {@link JobServiceContext#doAcknowledgeStartMessage}
611 * _EXECUTING -> call {@link #sendStopMessageLocked}}, but only if there are no callbacks
612 * in the message queue.
613 * _ENDING -> No point in doing anything here, so we ignore.
615 private void handleCancelLocked(String reason) {
616 if (JobSchedulerService.DEBUG) {
617 Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
618 + VERB_STRINGS[mVerb]);
624 applyStoppedReasonLocked(reason);
627 sendStopMessageLocked(reason);
633 Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
638 /** Process MSG_TIMEOUT here. */
639 private void handleOpTimeoutLocked() {
642 Slog.w(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
644 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while binding");
647 // Client unresponsive - wedged or failed to respond in time. We don't really
648 // know what happened so let's log it and notify the JobScheduler
649 // FINISHED/NO-RETRY.
650 Slog.w(TAG, "No response from client for onStartJob " +
651 mRunningJob != null ? mRunningJob.toShortString() : "<null>");
652 closeAndCleanupJobLocked(false /* needsReschedule */, "timed out while starting");
655 // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
656 Slog.w(TAG, "No response from client for onStopJob " +
657 mRunningJob != null ? mRunningJob.toShortString() : "<null>");
658 closeAndCleanupJobLocked(true /* needsReschedule */, "timed out while stopping");
661 // Not an error - client ran out of time.
662 Slog.i(TAG, "Client timed out while executing (no jobFinished received), " +
664 mRunningJob != null ? mRunningJob.toShortString() : "<null>");
665 mParams.setStopReason(JobParameters.REASON_TIMEOUT);
666 sendStopMessageLocked("timeout while executing");
669 Slog.e(TAG, "Handling timeout for an invalid job state: " +
670 mRunningJob != null ? mRunningJob.toShortString() : "<null>"
672 closeAndCleanupJobLocked(false /* needsReschedule */, "invalid timeout");
677 * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
680 private void sendStopMessageLocked(String reason) {
681 removeOpTimeOutLocked();
682 if (mVerb != VERB_EXECUTING) {
683 Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
684 closeAndCleanupJobLocked(false /* reschedule */, reason);
688 applyStoppedReasonLocked(reason);
689 mVerb = VERB_STOPPING;
690 scheduleOpTimeOutLocked();
691 service.stopJob(mParams);
692 } catch (RemoteException e) {
693 Slog.e(TAG, "Error sending onStopJob to client.", e);
694 // The job's host app apparently crashed during the job, so we should reschedule.
695 closeAndCleanupJobLocked(true /* reschedule */, "host crashed when trying to stop");
700 * The provided job has finished, either by calling
701 * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
702 * or from acknowledging the stop message we sent. Either way, we're done tracking it and
703 * we want to clean up internally.
705 private void closeAndCleanupJobLocked(boolean reschedule, String reason) {
706 final JobStatus completedJob;
707 if (mVerb == VERB_FINISHED) {
710 applyStoppedReasonLocked(reason);
711 completedJob = mRunningJob;
712 mJobPackageTracker.noteInactive(completedJob, mParams.getStopReason());
714 mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
715 mRunningJob.getSourceUid(), mParams.getStopReason());
716 } catch (RemoteException e) {
719 if (mWakeLock != null) {
722 mContext.unbindService(JobServiceContext.this);
725 mRunningCallback = null;
727 mVerb = VERB_FINISHED;
731 removeOpTimeOutLocked();
732 mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
735 private void applyStoppedReasonLocked(String reason) {
736 if (reason != null && mStoppedReason == null) {
737 mStoppedReason = reason;
738 mStoppedTime = SystemClock.elapsedRealtime();
739 if (mRunningCallback != null) {
740 mRunningCallback.mStoppedReason = mStoppedReason;
741 mRunningCallback.mStoppedTime = mStoppedTime;
747 * Called when sending a message to the client, over whose execution we have no control. If
748 * we haven't received a response in a certain amount of time, we want to give up and carry
751 private void scheduleOpTimeOutLocked() {
752 removeOpTimeOutLocked();
754 final long timeoutMillis;
757 timeoutMillis = EXECUTING_TIMESLICE_MILLIS;
761 timeoutMillis = OP_BIND_TIMEOUT_MILLIS;
765 timeoutMillis = OP_TIMEOUT_MILLIS;
769 Slog.d(TAG, "Scheduling time out for '" +
770 mRunningJob.getServiceComponent().getShortClassName() + "' jId: " +
771 mParams.getJobId() + ", in " + (timeoutMillis / 1000) + " s");
773 Message m = mCallbackHandler.obtainMessage(MSG_TIMEOUT, mRunningCallback);
774 mCallbackHandler.sendMessageDelayed(m, timeoutMillis);
775 mTimeoutElapsed = SystemClock.elapsedRealtime() + timeoutMillis;
779 private void removeOpTimeOutLocked() {
780 mCallbackHandler.removeMessages(MSG_TIMEOUT);