diff --git a/BatteryHookSample/src/main/AndroidManifest.xml b/BatteryHookSample/src/main/AndroidManifest.xml index 3bb1ed5..708080a 100644 --- a/BatteryHookSample/src/main/AndroidManifest.xml +++ b/BatteryHookSample/src/main/AndroidManifest.xml @@ -1,6 +1,11 @@ - + + + + + + - + - + - + diff --git a/BatteryHookSample/src/main/java/com/sample/battery/AppUtils.java b/BatteryHookSample/src/main/java/com/sample/battery/AppUtils.java new file mode 100644 index 0000000..692969d --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/AppUtils.java @@ -0,0 +1,72 @@ +package com.sample.battery; + +import android.app.Activity; +import android.app.ActivityManager; +import android.app.Application; +import android.content.ComponentCallbacks; +import android.content.Context; +import android.content.res.Configuration; +import android.util.DisplayMetrics; + +import java.util.List; + +public class AppUtils { + + public static boolean isAppBackground(Context context) { + final ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + final List list = manager.getRunningAppProcesses(); + + final String packageName = context.getPackageName(); + for (ActivityManager.RunningAppProcessInfo item : list) { + if (item.processName.equals(packageName)) { + return item.importance != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; + } + } + return false; + } + + private static float sDensity; + private static float sScaledDensity; + + /** + * 自定义 dp 适配方案 + * 需要在Activity的 onCreate 方法时调用 + * + * from toutiaotechblog + * @param designWdithDps 通常为 360dp + * @param application + * @param activity + */ + public static void customDensity(int designWdithDps, final Application application, Activity activity) { + DisplayMetrics appMetrics = application.getResources().getDisplayMetrics(); + if (sDensity == 0) { + sDensity = appMetrics.density; + sScaledDensity = appMetrics.scaledDensity; + application.registerComponentCallbacks(new ComponentCallbacks() { + @Override + public void onConfigurationChanged(Configuration newConfig) { + if (newConfig != null && newConfig.fontScale > 0) { + sScaledDensity = application.getResources().getDisplayMetrics().scaledDensity; + } + + } + + @Override + public void onLowMemory() { + + } + }); + + } + float scaleRatio = sScaledDensity / sDensity; + float targetDensity = (float) appMetrics.widthPixels / designWdithDps; + float targetScaledDensity = scaleRatio * targetDensity; + int targetDensityDpi = (int) (160 * targetDensity); + + DisplayMetrics activityMetrics = activity.getResources().getDisplayMetrics(); + appMetrics.density = activityMetrics.density = targetDensity; + appMetrics.scaledDensity = activityMetrics.scaledDensity = targetScaledDensity; + appMetrics.densityDpi = activityMetrics.densityDpi = targetDensityDpi; + + } +} diff --git a/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java b/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java index aa21293..f464a61 100644 --- a/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java +++ b/BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java @@ -1,29 +1,66 @@ package com.sample.battery; +import android.Manifest; +import android.annotation.SuppressLint; 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.Criteria; +import android.location.Location; +import android.location.LocationListener; +import android.location.LocationManager; +import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.os.HandlerThread; +import android.os.PowerManager; +import android.support.annotation.NonNull; +import android.support.v4.app.AlarmManagerCompat; +import android.support.v4.content.ContextCompat; +import android.util.Log; import android.view.View; import android.widget.Button; import com.sample.battery.R; -public class MainActivity extends Activity { - public static Context sContext; +import java.lang.reflect.Method; +import java.util.Calendar; +import java.util.logging.Logger; - Handler handler = new Handler(); +public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - sContext = getApplicationContext(); final Button hookAlarm = (Button) findViewById(R.id.hook_alarm); hookAlarm.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + new ProxyHook(alarmManager, "mService", new ProxyHook.InvokeBeforeListener() { + @Override + public void beforeInvoke(Method method, Object[] args) { + // 设置 Alarm + if (method.getName().equals("set")) { + // 不同版本参数类型的适配,获取应用堆栈等等 + Log.d("Hoook", "set Alarm", new Throwable()); + // 清除 Alarm + } else if (method.getName().equals("remove")) { + // 清除的逻辑 + Log.d("Hoook", "清除 Alarm", new Throwable()); + } + } + }); + if (alarmManager != null) { + PendingIntent pendingIntent = PendingIntent.getService(MainActivity.this, 1, new Intent(), PendingIntent.FLAG_ONE_SHOT); + alarmManager.cancel(pendingIntent); + AlarmManagerCompat.setAlarmClock(alarmManager, System.currentTimeMillis() + 10 * 1000, + pendingIntent, pendingIntent); + } } }); @@ -32,7 +69,31 @@ public void onClick(View v) { hookWakelock.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - + PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); + if (null != powerManager) { + new ProxyHook(powerManager, "mService", new ProxyHook.InvokeBeforeListener() { + @Override + public void beforeInvoke(Method method, Object[] args) { + // 申请 Wakelock + if (method.getName().equals("acquireWakeLock")) { + if (AppUtils.isAppBackground(SampleApplication.getCtx())) { + // 应用后台逻辑,获取应用堆栈等等 + Log.d("Hoook", "acquireWakeLock background", new Throwable()); + } else { + // 应用前台逻辑,获取应用堆栈等等 + Log.d("Hoook", "acquireWakeLock", new Throwable()); + } + // 释放 Wakelock + } else if (method.getName().equals("releaseWakeLock")) { + // 释放的逻辑 + Log.d("Hoook", "releaseWakeLock", new Throwable()); + } + } + }); + PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, MainActivity.class.getName()); + wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/); + wakeLock.release(); + } } }); @@ -41,10 +102,64 @@ public void onClick(View v) { hookGPS.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + final LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); + if (null != locationManager) { + new ProxyHook(locationManager, "mService", new ProxyHook.InvokeBeforeListener() { + @Override + public void beforeInvoke(Method method, Object[] args) { + // 请求一次定位 + if (method.getName().equals("requestLocationUpdates")) { + // 不同版本参数类型的适配,获取应用堆栈等等 + Log.d("Hoook", "requestLocationUpdates", new Throwable()); + // 清除 定位请求 + } else if (method.getName().equals("removeUpdates")) { + // 清除的逻辑 + Log.d("Hoook", "Location removeUpdates", new Throwable()); + } + } + }); + if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + Criteria criteria = new Criteria(); + final HandlerThread handlerThread = new HandlerThread("locationThread"); + handlerThread.start(); + LocationListener listener = new LocationListener() { + @Override + public void onLocationChanged(Location location) { + Log.d("Hoook", location.toString()); + handlerThread.quit(); + } + + @Override + public void onStatusChanged(String provider, int status, Bundle extras) { + + } + @Override + public void onProviderEnabled(String provider) { + + } + + @Override + public void onProviderDisabled(String provider) { + handlerThread.quit(); + locationManager.removeUpdates(this); + } + }; + locationManager.requestSingleUpdate(criteria, listener, handlerThread.getLooper()); + } + + } } }); - + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && + ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION}, 0); + } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + Log.d("Hoook", "授权成功!"); + } } diff --git a/BatteryHookSample/src/main/java/com/sample/battery/ProxyHook.java b/BatteryHookSample/src/main/java/com/sample/battery/ProxyHook.java new file mode 100644 index 0000000..58af0a7 --- /dev/null +++ b/BatteryHookSample/src/main/java/com/sample/battery/ProxyHook.java @@ -0,0 +1,53 @@ +package com.sample.battery; + +import android.util.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * 代理Hook的属性,并在方法调用前回调 beforeInvoke + */ +public class ProxyHook implements InvocationHandler { + private Object mHookTarget; + private InvokeBeforeListener mListener; + + public ProxyHook(Object hookRef, String field, InvokeBeforeListener listener) { + if (null == hookRef) { + Log.e("Hoook", "hookRef is not exist"); + return; + } + try { + Field fieldRef = hookRef.getClass().getDeclaredField(field); + fieldRef.setAccessible(true); + + mHookTarget = fieldRef.get(hookRef); + + Object newObj = Proxy.newProxyInstance(this.getClass().getClassLoader(), mHookTarget.getClass().getInterfaces(), this); + fieldRef.set(hookRef, newObj); + this.mListener = listener; + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + public void setListener(InvokeBeforeListener listener) { + mListener = listener; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if (null != mListener) { + mListener.beforeInvoke(method, args); + } + return method.invoke(mHookTarget, args); + } + + public interface InvokeBeforeListener { + void beforeInvoke(Method method, Object[] args); + } +} diff --git a/BatteryHookSample/src/main/java/com/sample/battery/SampleApplication.java b/BatteryHookSample/src/main/java/com/sample/battery/SampleApplication.java index e33f068..d23da97 100644 --- a/BatteryHookSample/src/main/java/com/sample/battery/SampleApplication.java +++ b/BatteryHookSample/src/main/java/com/sample/battery/SampleApplication.java @@ -5,6 +5,7 @@ import android.content.Context; public class SampleApplication extends Application { + private static Context mCtx; public SampleApplication() { } @@ -15,8 +16,13 @@ public void onCreate() { } + public static Context getCtx() { + return mCtx; + } + @Override protected void attachBaseContext(final Context base) { + mCtx = base; super.attachBaseContext(base); } } diff --git a/build.gradle b/build.gradle index cdf1d5d..128c1b9 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' + classpath 'com.android.tools.build:gradle:3.2.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files