/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import android.os.SystemClock; import android.os.ConditionVariable; /** * Utility class that you can call on with a timeout, and get called back * after a given time, dealing correctly with restarting the timeout. * *
For example, this class is used by the android.os.Vibrator class. */ abstract class ResettableTimeout { /** * Override this do what you need to do when it's starting * This is called with the monitor on this method held, so be careful. * * @param alreadyOn is true if it's currently running */ public abstract void on(boolean alreadyOn); /** * Override this to do what you need to do when it's stopping. * This is called with the monitor on this method held, so be careful. */ public abstract void off(); /** * Does the following steps. *
1. Call on()
*2. Start the timer.
*3. At the timeout, calls off()
*
If you call this again, the timeout is reset to the new one
*/ public void go(long milliseconds) { synchronized (this) { mOffAt = SystemClock.uptimeMillis() + milliseconds; boolean alreadyOn; // By starting the thread first and waiting, we ensure that if the // thread to stop it can't start, we don't turn the vibrator on // forever. This still isn't really sufficient, because we don't // have another processor watching us. We really should have a // service for this in case our process crashes. if (mThread == null) { alreadyOn = false; mLock.close(); mThread = new T(); mThread.start(); mLock.block(); mOffCalled = false; } else { alreadyOn = true; // poke the thread so it gets the new timeout. mThread.interrupt(); } on(alreadyOn); } } /** * Cancel the timeout and call off now. */ public void cancel() { synchronized (this) { mOffAt = 0; if (mThread != null) { mThread.interrupt(); mThread = null; } if (!mOffCalled) { mOffCalled = true; off(); } } } private class T extends Thread { public void run() { mLock.open(); while (true) { long diff; synchronized (this) { diff = mOffAt - SystemClock.uptimeMillis(); if (diff <= 0) { mOffCalled = true; off(); mThread = null; break; } } try { sleep(diff); } catch (InterruptedException e) { } } } } private ConditionVariable mLock = new ConditionVariable(); // turn it off at this time. private volatile long mOffAt; private volatile boolean mOffCalled; private Thread mThread; }