Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions BatteryHookSample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.sample.battery"
xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sample.battery">

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name="com.sample.battery.SampleApplication"
Expand All @@ -9,13 +14,11 @@
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true">
<activity
android:name="com.sample.battery.MainActivity"
>
<activity android:name="com.sample.battery.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
Expand Down
72 changes: 72 additions & 0 deletions BatteryHookSample/src/main/java/com/sample/battery/AppUtils.java
Original file line number Diff line number Diff line change
@@ -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<ActivityManager.RunningAppProcessInfo> 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;

}
}
129 changes: 122 additions & 7 deletions BatteryHookSample/src/main/java/com/sample/battery/MainActivity.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
});

Expand All @@ -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();
}

}
});
Expand All @@ -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", "授权成功!");
}
}
53 changes: 53 additions & 0 deletions BatteryHookSample/src/main/java/com/sample/battery/ProxyHook.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.content.Context;

public class SampleApplication extends Application {
private static Context mCtx;

public SampleApplication() {
}
Expand All @@ -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);
}
}
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down