blob: 511af941bf761ff366c4bfb1219d731f5066acb9 [file] [log] [blame]
/*
* 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.compat.annotation.UnsupportedAppUsage;
import android.os.ConditionVariable;
import android.os.SystemClock;
/**
* Utility class that you can call on with a timeout, and get called back
* after a given time, dealing correctly with restarting the timeout.
*
* <p>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.
* <p>1. Call on()</p>
* <p>2. Start the timer.</p>
* <p>3. At the timeout, calls off()<p>
* <p>If you call this again, the timeout is reset to the new one</p>
*/
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) {
}
}
}
}
@UnsupportedAppUsage
private ConditionVariable mLock = new ConditionVariable();
// turn it off at this time.
@UnsupportedAppUsage
private volatile long mOffAt;
private volatile boolean mOffCalled;
private Thread mThread;
}