blob: c5539b27c3b813efc73ba28788eec2b9df032f40 [file] [log] [blame]
/*
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.util;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Vector;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.util.Log;
import edu.umich.PowerTutor.PowerNotifications;
@SuppressWarnings("unchecked")
public class NotificationService {
private static final String TAG = "NotificationService";
/* We haven't tried to install the hook yet. */
private static final int STATE_INIT = 0;
/* The hook was installed successfully and we should be receiving power
* related notifications from the battery service.
*/
private static final int STATE_HOOK_INSTALLED = 1;
/* The hook failed to install. This should be the case for most phones as a
* hack is required to get this to work.
*/
private static final int STATE_HOOK_FAILED = 2;
private static int hookState = STATE_INIT;
private static Binder notifier = new NotificationForwarder();
private static Vector<PowerNotifications> hooks =
new Vector<PowerNotifications>();
private static Method methodGetService;
static {
try {
Class classServiceManager = Class.forName("android.os.ServiceManager");
methodGetService = classServiceManager.getMethod("getService", String.class);
} catch(NoSuchMethodException e) {
Log.w(TAG, "Could not find method gerService");
} catch(ClassNotFoundException e) {
Log.w(TAG, "Could not find class android.os.ServiceManager");
}
}
private static IBinder getBatteryService() {
if(methodGetService == null) return null;
try {
return (IBinder)methodGetService.invoke(null, "batteryhook");
} catch(InvocationTargetException e) {
Log.w(TAG, "Call to get service failed");
} catch(IllegalAccessException e) {
Log.w(TAG, "Call to get service failed");
}
return null;
}
public static boolean available() {
synchronized(hooks) {
if(hookState == STATE_INIT) {
return getBatteryService() != null;
}
return hookState == STATE_HOOK_INSTALLED;
}
}
public static void addHook(PowerNotifications notif) {
synchronized(hooks) {
if(hookState == STATE_INIT) {
installHook();
}
if(hookState != STATE_HOOK_INSTALLED) {
Log.w(TAG, "Attempted to add hook though no " +
"notification service available");
} else {
hooks.add(notif);
}
}
}
public static void removeHook(PowerNotifications notif) {
synchronized(hooks) {
hooks.remove(notif);
}
}
private static void installHook() {
Parcel outBinder = Parcel.obtain();
outBinder.writeStrongBinder(notifier);
hookState = STATE_HOOK_FAILED;
try {
IBinder batteryHook = getBatteryService();
if(batteryHook == null) {
/* This should be the case on un-hacked phone. Maybe one day
* phones will support this service or similar by default.
*/
Log.i(TAG, "No power notification hook service installed");
} else if(!batteryHook.transact(0, outBinder, null, 0)) {
Log.w(TAG, "Failed to register forwarder");
} else {
hookState = STATE_HOOK_INSTALLED;
}
} catch(RemoteException e) {
Log.w(TAG, "Failed to register forwarder");
}
outBinder.recycle();
}
/* Class responsible for forwarding power notifications to registered
* hooks.
*/
private static class NotificationForwarder extends DefaultReceiver {
public boolean onTransact(int code, Parcel data,
Parcel reply, int flags) throws RemoteException {
synchronized(hooks) {
for(Iterator<PowerNotifications> iter = hooks.iterator();
iter.hasNext(); ) {
Parcel junk = Parcel.obtain();
try {
iter.next().asBinder().transact(code, data, junk, flags);
} catch(RemoteException e) {
iter.remove();
}
data.setDataPosition(0);
junk.recycle();
}
}
return super.onTransact(code, data, reply, flags);
}
}
/* If you only want to receive a subset of the notifications just extend this
* class and override the methods you care about.
*/
public static class DefaultReceiver extends PowerNotifications.Stub {
public void noteSystemMediaCall(int uid) {}
public void noteStartMedia(int uid, int id) {}
public void noteStopMedia(int uid, int id) {}
public void noteVideoSize(int uid, int id, int width, int height) {}
public void noteStartWakelock(int uid, String name, int type) {}
public void noteStopWakelock(int uid, String name, int type) {}
public void noteStartSensor(int uid, int sensor) {}
public void noteStopSensor(int uid, int sensor) {}
public void noteStartGps(int uid) {}
public void noteStopGps(int uid) {}
public void noteScreenOn() {}
public void noteScreenBrightness(int brightness) {}
public void noteScreenOff() {}
public void noteInputEvent() {}
public void noteUserActivity(int uid, int event) {}
public void notePhoneOn() {}
public void notePhoneOff() {}
public void notePhoneDataConnectionState(int dataType, boolean hasData) {}
public void noteWifiOn(int uid) {}
public void noteWifiOff(int uid) {}
public void noteWifiRunning() {}
public void noteWifiStopped() {}
public void noteBluetoothOn() {}
public void noteBluetoothOff() {}
public void noteFullWifiLockAcquired(int uid) {}
public void noteFullWifiLockReleased(int uid) {}
public void noteScanWifiLockAcquired(int uid) {}
public void noteScanWifiLockReleased(int uid) {}
public void noteWifiMulticastEnabled(int uid) {}
public void noteWifiMulticastDisabled(int uid) {}
public void setOnBattery(boolean onBattery, int level) {}
public void recordCurrentLevel(int level) {}
public void noteVideoOn(int uid) {}
public void noteVideoOff(int uid) {}
public void noteAudioOn(int uid) {}
public void noteAudioOff(int uid) {}
}
/* Useful for debugging purposes. */
public static class PrintNotifications extends PowerNotifications.Stub {
public void noteSystemMediaCall(int uid) {
System.out.println("System media call[uid=" + uid + "]");
}
public void noteStartMedia(int uid, int id) {
System.out.println("Start media[uid=" + uid + ", id=" + id + "]");
}
public void noteStopMedia(int uid, int id) {
System.out.println("Stop media[uid=" + uid + ", id=" + id + "]");
}
public void noteVideoSize(int uid, int id, int width, int height) {
System.out.println("Video size[uid=" + uid + ", id=" + id +
", width=" + width + ", height=" + height + "]");
}
public void noteStartWakelock(int uid, String name, int type) {
System.out.println("Start wakelock[uid=" + uid + ", name=" + name +
", type=" + type + "]");
}
public void noteStopWakelock(int uid, String name, int type) {
System.out.println("Stop wakelock[uid=" + uid + ", name=" + name +
", type=" + type + "]");
}
public void noteStartSensor(int uid, int sensor) {
System.out.println("noteStartSensor[uid=" + uid + ", sensor=" + sensor +
"]");
}
public void noteStopSensor(int uid, int sensor) {
System.out.println("noteStopSensor[uid=" + uid + ", sensor=" + sensor +
"]");
}
public void noteStartGps(int uid) {
System.out.println("noteStartGps[uid=" + uid + "]");
}
public void noteStopGps(int uid) {
System.out.println("noteStopGps[uid=" + uid + "]");
}
public void noteScreenOn() {
System.out.println("noteScreenOn");
}
public void noteScreenBrightness(int brightness) {
System.out.println("noteScreenBrightness[brightness=" + brightness + "]");
}
public void noteScreenOff() {
System.out.println("noteScreenOff");
}
public void noteInputEvent() {
System.out.println("noteInputEvent");
}
public void noteUserActivity(int uid, int event) {
System.out.println("noteUserActivity[uid=" + uid + ", event=" + event +
"]");
}
public void notePhoneOn() {
System.out.println("notePhoneOn");
}
public void notePhoneOff() {
System.out.println("notePhoneOff");
}
public void notePhoneDataConnectionState(int dataType, boolean hasData) {
System.out.println("notePhoneDataConnectionState[dataType=" + dataType +
", hasData=" + hasData + "]");
}
public void notePhoneState(int phoneState) {
System.out.println("notePhoneState[phoneState=" + phoneState + "]");
}
public void noteWifiOn(int uid) {
System.out.println("noteWifiOn[uid=" + uid + "]");
}
public void noteWifiOff(int uid) {
System.out.println("noteWifiOff[uid=" + uid + "]");
}
public void noteWifiRunning() {
System.out.println("noteWifiRunning");
}
public void noteWifiStopped() {
System.out.println("noteWifiStopped");
}
public void noteBluetoothOn() {
System.out.println("noteBluetoothOn");
}
public void noteBluetoothOff() {
System.out.println("noteBluetoothOff");
}
public void noteFullWifiLockAcquired(int uid) {
System.out.println("noteFullWifiLockAcquired[uid=" + uid + "]");
}
public void noteFullWifiLockReleased(int uid) {
System.out.println("noteFullWifiLockReleased[uid=" + uid + "]");
}
public void noteScanWifiLockAcquired(int uid) {
System.out.println("noteScanWifiLockAcquired[uid=" + uid + "]");
}
public void noteScanWifiLockReleased(int uid) {
System.out.println("noteScanWifiLockReleased[uid=" + uid + "]");
}
public void noteWifiMulticastEnabled(int uid) {
System.out.println("noteWifiMulticastEnabled[uid=" + uid + "]");
}
public void noteWifiMulticastDisabled(int uid) {
System.out.println("noteWifiMulticastDisabled[uid=" + uid + "]");
}
public void setOnBattery(boolean onBattery, int level) {
System.out.println("setOnBattery[onBattery=" + onBattery + ", level=" +
level + "]");
}
public void recordCurrentLevel(int level) {
System.out.println("recordCurrentLevel[level=" + level + "]");
}
public void noteVideoOn(int uid) {
System.out.println("noteVideoOn[uid=" + uid + "]");
}
public void noteVideoOff(int uid) {
System.out.println("noteVideoOff[uid=" + uid + "]");
}
public void noteAudioOn(int uid) {
System.out.println("noteAudioOn[uid=" + uid + "]");
}
public void noteAudioOff(int uid) {
System.out.println("noteAudioOff[uid=" + uid + "]");
}
}
}