diff --git a/BatteryHookSample/src/main/AndroidManifest.xml b/BatteryHookSample/src/main/AndroidManifest.xml index 3bb1ed5..15ab60b 100644 --- a/BatteryHookSample/src/main/AndroidManifest.xml +++ b/BatteryHookSample/src/main/AndroidManifest.xml @@ -2,6 +2,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java b/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java index aa21293..23f83cf 100644 --- a/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java +++ b/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java @@ -1,18 +1,35 @@ package com.sample.battery; import android.app.Activity; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; import android.os.Bundle; -import android.os.Handler; +import android.os.PowerManager; +import android.support.v4.app.ActivityCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; import android.view.View; import android.widget.Button; -import com.sample.battery.R; +import com.sample.battery.alarm.IAlarmManagerHook; +import com.sample.battery.alarm.MyAlarmReceiver; +import com.sample.battery.gps.ILocationManagerHook; +import com.sample.battery.wakelock.IPowerManagerHook; + +import static android.Manifest.permission.ACCESS_FINE_LOCATION; public class MainActivity extends Activity { public static Context sContext; - Handler handler = new Handler(); + private PowerManager.WakeLock wakeLock; + private LocationManager locationManager; + private LocationListener locationListener; @Override protected void onCreate(Bundle savedInstanceState) { @@ -23,7 +40,7 @@ protected void onCreate(Bundle savedInstanceState) { hookAlarm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - + new IAlarmManagerHook(sContext).onInstall(); } }); @@ -32,8 +49,7 @@ public void onClick(View v) { hookWakelock.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - - + new IPowerManagerHook(sContext).onInstall(); } }); @@ -41,10 +57,109 @@ public void onClick(View v) { hookGPS.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + new ILocationManagerHook(sContext).onInstall(); + } + }); + + final Button wakelockAcquire = (Button) findViewById(R.id.wakelock_acquire); + wakelockAcquire.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + PowerManager powerManager = (PowerManager) sContext.getSystemService(POWER_SERVICE); + wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getClass().getName());//持有唤醒锁 + wakeLock.setReferenceCounted(false); + wakeLock.acquire(60 * 1000); //亮屏60s } }); + final Button wakelockRelease = (Button) findViewById(R.id.wakelock_release); + wakelockRelease.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (wakeLock != null){ + wakeLock.release();//释放锁,灭屏 + } + } + }); + + final Button alarmSet = (Button) findViewById(R.id.alarm_set); + alarmSet.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AlarmManager alarmService = (AlarmManager) sContext.getSystemService(ALARM_SERVICE); + Intent alarmIntent = new Intent(sContext, MyAlarmReceiver.class).setAction("intent_alarm"); + PendingIntent broadcast = PendingIntent.getBroadcast(sContext, 0, alarmIntent, 0);//通过广播接收 + alarmService.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + 2000, broadcast);//INTERVAL毫秒后触 + } + }); + + final Button alarmCancel = (Button) findViewById(R.id.alarm_cancel); + alarmCancel.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + AlarmManager alarmService = (AlarmManager) sContext.getSystemService(ALARM_SERVICE); + Intent alarmIntent = new Intent(sContext, MyAlarmReceiver.class).setAction("intent_alarm"); + PendingIntent broadcast = PendingIntent.getBroadcast(sContext, 0, alarmIntent, 0); + alarmService.cancel(broadcast); + } + }); + + final Button gpsRequest = (Button) findViewById(R.id.gps_request); + gpsRequest.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + + if (ContextCompat.checkSelfPermission(MainActivity.this, ACCESS_FINE_LOCATION) + != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(MainActivity.this, + new String[]{ACCESS_FINE_LOCATION}, 100); + } else { + locationManager = (LocationManager) sContext.getSystemService(LOCATION_SERVICE); + locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 3000, 1, locationListener); + } + + } + }); + + final Button gpsRemove = (Button) findViewById(R.id.gps_remove); + gpsRemove.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (locationManager != null && locationListener != null) { + // 关闭程序时将监听器移除 + locationManager.removeUpdates(locationListener); + } + } + }); + + locationListener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + Log.e("HOOOOOOOOK", location.toString()); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + + } + + @Override + public void onProviderEnabled(String provider) { + + } + + @Override + public void onProviderDisabled(String provider) { + + } + }; } + @Override + public void onRequestPermissionsResult( + int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + + } } diff --git a/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHook.java b/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHook.java new file mode 100644 index 0000000..3895e3a --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHook.java @@ -0,0 +1,45 @@ +package com.sample.battery.alarm; + +import android.content.Context; +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.ProxyHook; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; + +public class IAlarmManagerHook extends ProxyHook { + + public IAlarmManagerHook(Context context) { + super(context); + } + + @Override + public BaseHookHandle createBaseHookHandle() { + return new IAlarmManagerHookHandle(); + } + + @Override + public void onInstall() { + Object oldObj = mHostContext.getSystemService(Context.ALARM_SERVICE); + Class clazz = oldObj.getClass(); + + try { + Field field = clazz.getDeclaredField("mService"); + field.setAccessible(true); + + final Object mService = field.get(oldObj); + setProxyObj(mService); + + Object newObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), mService.getClass().getInterfaces(), this); + field.set(oldObj, newObj); + + } catch (NoSuchFieldException e) { + Log.e("HOOOOOOOOK", "IAlarmManager is null"); + } catch (IllegalAccessException e){ + e.fillInStackTrace(); + } + + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHookHandle.java b/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHookHandle.java new file mode 100644 index 0000000..738aa06 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/alarm/IAlarmManagerHookHandle.java @@ -0,0 +1,55 @@ +package com.sample.battery.alarm; + +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.HookedMethodHandler; +import com.sample.battery.hook.Utils; + +import java.lang.reflect.Method; + +public class IAlarmManagerHookHandle extends BaseHookHandle { + + @Override + public void init() { + sHookedMethodHandlers.put("set", new setAlarmManagerService()); + sHookedMethodHandlers.put("remove", new removeAlarmManagerService()); + } + + public class setAlarmManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 设置 Alarm + Log.e("HOOOOOOOOK", "--set--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + + // 前台:单个Alarm每小时不能启动超20次 + // 后台:单个Alarm每小时不能启动超10次 + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } + + public class removeAlarmManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 清除 Alarm + Log.e("HOOOOOOOOK", "--remove--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } + + +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/alarm/MyAlarmReceiver.java b/BatteryHookSample/src/main/java/com/sample/battery/alarm/MyAlarmReceiver.java new file mode 100644 index 0000000..f4b9e8c --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/alarm/MyAlarmReceiver.java @@ -0,0 +1,12 @@ +package com.sample.battery.alarm; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public class MyAlarmReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHook.java b/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHook.java new file mode 100644 index 0000000..302d88e --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHook.java @@ -0,0 +1,45 @@ +package com.sample.battery.gps; + +import android.content.Context; +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.ProxyHook; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; + +public class ILocationManagerHook extends ProxyHook { + + public ILocationManagerHook(Context context) { + super(context); + } + + @Override + public BaseHookHandle createBaseHookHandle() { + return new ILocationManagerHookHandle(); + } + + @Override + public void onInstall() { + Object oldObj = mHostContext.getSystemService(Context.LOCATION_SERVICE); + Class clazz = oldObj.getClass(); + + try { + Field field = clazz.getDeclaredField("mService"); + field.setAccessible(true); + + final Object mService = field.get(oldObj); + setProxyObj(mService); + + Object newObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), mService.getClass().getInterfaces(), this); + field.set(oldObj, newObj); + + } catch (NoSuchFieldException e) { + Log.e("HOOOOOOOOK", "ILocationManager is null"); + } catch (IllegalAccessException e){ + e.fillInStackTrace(); + } + + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHookHandle.java b/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHookHandle.java new file mode 100644 index 0000000..84ac452 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/gps/ILocationManagerHookHandle.java @@ -0,0 +1,55 @@ +package com.sample.battery.gps; + +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.HookedMethodHandler; +import com.sample.battery.hook.Utils; + +import java.lang.reflect.Method; + +public class ILocationManagerHookHandle extends BaseHookHandle { + + @Override + public void init() { + sHookedMethodHandlers.put("requestLocationUpdates", new requestLocationManagerService()); + sHookedMethodHandlers.put("removeUpdates", new removeLocationManagerService()); + } + + public class requestLocationManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 获取定位 + Log.e("HOOOOOOOOK", "--requestLocationUpdates--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + + // 前台:每小时使用不能超过30分钟 + // 后台:每小时使用不能超过15分钟 + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } + + public class removeLocationManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 清除监听 + Log.e("HOOOOOOOOK", "--removeUpdates--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } + + +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/hook/BaseHookHandle.java b/BatteryHookSample/src/main/java/com/sample/battery/hook/BaseHookHandle.java new file mode 100644 index 0000000..aeb6360 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/hook/BaseHookHandle.java @@ -0,0 +1,24 @@ +package com.sample.battery.hook; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +public abstract class BaseHookHandle { + + protected Map sHookedMethodHandlers = new HashMap(5); + + public abstract void init(); + + public BaseHookHandle() { + init(); + } + + public HookedMethodHandler getHookedMethodHandler(Method method) { + if (method != null) { + return sHookedMethodHandlers.get(method.getName()); + } else { + return null; + } + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/hook/Hook.java b/BatteryHookSample/src/main/java/com/sample/battery/hook/Hook.java new file mode 100644 index 0000000..29a53f8 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/hook/Hook.java @@ -0,0 +1,19 @@ +package com.sample.battery.hook; + +import android.content.Context; + +public abstract class Hook { + + protected BaseHookHandle mBaseHookHandle; + + protected Context mHostContext; + + public Hook(Context context) { + mHostContext = context; + mBaseHookHandle = createBaseHookHandle(); + } + + public abstract BaseHookHandle createBaseHookHandle(); + + public abstract void onInstall(); +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/hook/HookedMethodHandler.java b/BatteryHookSample/src/main/java/com/sample/battery/hook/HookedMethodHandler.java new file mode 100644 index 0000000..e6c6f2b --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/hook/HookedMethodHandler.java @@ -0,0 +1,23 @@ +package com.sample.battery.hook; + +import java.lang.reflect.Method; + +public class HookedMethodHandler { + + public synchronized Object doHookInner(Object receiver, Method method, Object[] args) throws Throwable { + boolean suc = beforeInvoke(receiver, method, args); + Object invokeResult = null; + if (!suc) { + invokeResult = method.invoke(receiver, args); + } + afterInvoke(receiver, method, args, invokeResult); + return invokeResult; + } + + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + return false; + } + + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/hook/ProxyHook.java b/BatteryHookSample/src/main/java/com/sample/battery/hook/ProxyHook.java new file mode 100644 index 0000000..0de14f1 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/hook/ProxyHook.java @@ -0,0 +1,31 @@ +package com.sample.battery.hook; + +import android.content.Context; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; + +public abstract class ProxyHook extends Hook implements InvocationHandler { + + /** + * 要代理的真实对象 + */ + private Object proxyObj; + + public ProxyHook(Context context) { + super(context); + } + + public void setProxyObj(Object proxyObj) { + this.proxyObj = proxyObj; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + HookedMethodHandler hookedMethodHandler = mBaseHookHandle.getHookedMethodHandler(method); + if (hookedMethodHandler != null){ + return hookedMethodHandler.doHookInner(proxyObj, method, args); + } + return method.invoke(proxyObj, args); + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/hook/Utils.java b/BatteryHookSample/src/main/java/com/sample/battery/hook/Utils.java new file mode 100644 index 0000000..9e3bfb0 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/hook/Utils.java @@ -0,0 +1,31 @@ +package com.sample.battery.hook; + +public class Utils { + + + public static String getStackTrace() { + StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); + if (null != stackTrace && stackTrace.length > 0) { + StringBuilder sb = new StringBuilder(); + + StackTraceElement stackTraceElement; + String fileName; + String className; + String methodName; + int lineNumber; + for (int i = 1; i < stackTrace.length; i++) { + stackTraceElement = stackTrace[i]; + className = stackTraceElement.getClassName(); + if (Utils.class.getName().equals(className)) { + continue; + } + methodName = stackTraceElement.getMethodName(); + fileName = stackTraceElement.getFileName(); + lineNumber = stackTraceElement.getLineNumber(); + sb.append(className).append(".").append(methodName).append("(").append(fileName).append(":").append(lineNumber).append(")\n"); + } + return sb.toString(); + } + return ""; + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHook.java b/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHook.java new file mode 100644 index 0000000..f0504ac --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHook.java @@ -0,0 +1,45 @@ +package com.sample.battery.wakelock; + +import android.content.Context; +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.ProxyHook; + +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; + +public class IPowerManagerHook extends ProxyHook { + + public IPowerManagerHook(Context context) { + super(context); + } + + @Override + public BaseHookHandle createBaseHookHandle() { + return new IPowerManagerHookHandle(); + } + + @Override + public void onInstall() { + Object oldObj = mHostContext.getSystemService(Context.POWER_SERVICE); + Class clazz = oldObj.getClass(); + + try { + Field field = clazz.getDeclaredField("mService"); + field.setAccessible(true); + + final Object mService = field.get(oldObj); + setProxyObj(mService); + + Object newObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), mService.getClass().getInterfaces(), this); + field.set(oldObj, newObj); + + } catch (NoSuchFieldException e) { + Log.e("HOOOOOOOOK", "IPowerManager is null"); + } catch (IllegalAccessException e){ + e.fillInStackTrace(); + } + + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHookHandle.java b/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHookHandle.java new file mode 100644 index 0000000..f07dfe9 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/wakelock/IPowerManagerHookHandle.java @@ -0,0 +1,52 @@ +package com.sample.battery.wakelock; + +import android.util.Log; + +import com.sample.battery.hook.BaseHookHandle; +import com.sample.battery.hook.HookedMethodHandler; +import com.sample.battery.hook.Utils; + +import java.lang.reflect.Method; + +public class IPowerManagerHookHandle extends BaseHookHandle { + + @Override + public void init() { + sHookedMethodHandlers.put("acquireWakeLock", new acquirePowerManagerService()); + sHookedMethodHandlers.put("releaseWakeLock", new releasePowerManagerService()); + } + + public class acquirePowerManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 申请 Wakelock + Log.e("HOOOOOOOOK", "--acquireWakeLock--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + // 前台:单个wakelock每小时不超过30分钟或者20次 + // 后台:单个wakelock每小时不超过10分钟或者12次 + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } + + public class releasePowerManagerService extends HookedMethodHandler { + + @Override + protected boolean beforeInvoke(Object receiver, Method method, Object[] args) throws Throwable { + // 释放 Wakelock + Log.e("HOOOOOOOOK", "--releaseWakeLock--"); + Log.e("HOOOOOOOOK", Utils.getStackTrace()); + return super.beforeInvoke(receiver, method, args); + } + + @Override + protected void afterInvoke(Object receiver, Method method, Object[] args, Object invokeResult) throws Throwable { + super.afterInvoke(receiver, method, args, invokeResult); + } + } +} \ No newline at end of file diff --git a/BatteryHookSample/src/main/res/layout/activity_main.xml b/BatteryHookSample/src/main/res/layout/activity_main.xml index 3c92d55..9874765 100644 --- a/BatteryHookSample/src/main/res/layout/activity_main.xml +++ b/BatteryHookSample/src/main/res/layout/activity_main.xml @@ -8,28 +8,79 @@ tools:context="com.sample.battery.MainActivity">