/*
Copyright (C) 2011 The University of Michigan
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Please send inquiries to powertutor@umich.edu
*/
package edu.umich.PowerTutor.components;
import edu.umich.PowerTutor.service.IterationData;
import android.os.SystemClock;
import android.util.Log;
public abstract class PowerComponent extends Thread {
private final String TAG = "PowerComponent";
/* Extending classes need to override the calculateIteration function. It
* should calculate the data point for the given component in a timely
* manner (under 1 second, longer times will cause data to be missed).
* The iteration parameter can be ignored in most cases.
*/
protected abstract IterationData calculateIteration(long iteration);
/* Extending classes should provide a recognizable name for this component
* to be used when writing to logs.
*/
public abstract String getComponentName();
/* Returns true if this component collects usage information per uid.
*/
public boolean hasUidInformation() {
return false;
}
/* Called when the thread running this interface is asked to exit.
*/
protected void onExit() {
}
/* In general we should only need to buffer two data elements.
*/
private IterationData data1;
private IterationData data2;
private long iteration1;
private long iteration2;
protected long beginTime;
protected long iterationInterval;
public PowerComponent() {
setDaemon(true);
}
/* This is called once at the begginning of the daemon loop.
*/
public void init(long beginTime, long iterationInterval) {
this.beginTime = beginTime;
this.iterationInterval = iterationInterval;
data1 = data2 = null;
iteration1 = iteration2 = -1;
}
/* Runs the daemon loop that collects data for this component. */
public void run() {
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_MORE_FAVORABLE);
for(long iter = 0; !Thread.interrupted(); ) {
/* Hand off to the client class to actually calculate the information
* we want for this component.
*/
IterationData data = calculateIteration(iter);
if(data != null) {
synchronized(this) {
if(iteration1 < iteration2) {
iteration1 = iter;
data1 = data;
} else {
iteration2 = iter;
data2 = data;
}
}
}
if(interrupted()) {
break;
}
long curTime = SystemClock.elapsedRealtime();
/* Compute the next iteration that we can make the start of. */
long oldIter = iter;
iter = (long)Math.max(iter + 1,
1 + (curTime - beginTime) / iterationInterval);
if(oldIter + 1 != iter) {
Log.w(TAG, "[" + getComponentName() + "] Had to skip from iteration " +
oldIter + " to " + iter);
}
/* Sleep until the next iteration completes. */
try {
sleep(beginTime + iter * iterationInterval - curTime);
} catch(InterruptedException e) {
break;
}
}
onExit();
}
/* Returns the data point for the given iteration. This method will be called
with a strictly increasing iteration parameter.
*/
public IterationData getData(long iteration) {
synchronized(this) {
IterationData ret = null;
if(iteration == iteration1) ret = data1;
if(iteration == iteration2) ret = data2;
if(iteration1 <= iteration) {
data1 = null;
iteration1 = -1;
}
if(iteration2 <= iteration) {
data2 = null;
iteration2 = -1;
}
if(ret == null) {
Log.w(TAG, "[" + getComponentName() + "] Could not find data for " +
"requested iteration");
}
return ret;
}
}
}