├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── gradle.xml
├── misc.xml
└── vcs.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── release
│ └── output-metadata.json
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ └── xposed_init
│ ├── java
│ │ └── cn
│ │ │ └── myflv
│ │ │ └── android
│ │ │ └── noanr
│ │ │ ├── Hook.java
│ │ │ ├── entity
│ │ │ ├── ClassEnum.java
│ │ │ ├── FieldEnum.java
│ │ │ └── MethodEnum.java
│ │ │ └── utils
│ │ │ ├── AnrUtil.java
│ │ │ ├── AppOpsUtil.java
│ │ │ ├── AppUtil.java
│ │ │ ├── BroadcastUtil.java
│ │ │ ├── ClassUtil.java
│ │ │ ├── ObjectUtil.java
│ │ │ └── ProcessUtil.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ └── ic_launcher_background.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ └── values
│ │ ├── array.xml
│ │ ├── colors.xml
│ │ └── strings.xml
│ └── test
│ └── java
│ └── cn
│ └── myflv
│ └── android
│ └── noanr
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── release
├── NoANR_12(Fix).apk
└── output-metadata.json
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # NoANR
2 | 通过AppOps设置WAKE_LOCK为ignore时屏蔽广播从而解决因为广播ANR导致的卡通话和安装慢等等隐藏问题
3 | 发生ANR时判断为用户应用时拦截ANR
4 | 更新内容
5 | 兼容安卓9-10版本
6 | 最佳安卓版本11-12
7 | 安卓7-8版本未知(无人反馈)
8 | 版本修改至2.3
9 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | }
4 |
5 | android {
6 | compileSdk 32
7 |
8 | defaultConfig {
9 | applicationId "cn.myflv.android.noanr"
10 | minSdk 23
11 | targetSdk 32
12 | versionCode 10
13 | versionName "2.4-alpha"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | }
28 |
29 | dependencies {
30 | compileOnly 'de.robv.android.xposed:api:82'
31 | compileOnly 'de.robv.android.xposed:api:82:sources'
32 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/release/output-metadata.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 3,
3 | "artifactType": {
4 | "type": "APK",
5 | "kind": "Directory"
6 | },
7 | "applicationId": "cn.myflv.android.noanr",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 10,
15 | "versionName": "2.4-alpha",
16 | "outputFile": "app-release.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
14 |
15 |
18 |
19 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | cn.myflv.android.noanr.Hook
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noanr/Hook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noanr;
2 |
3 | import android.os.Build;
4 | import android.util.Log;
5 |
6 | import java.io.BufferedReader;
7 | import java.io.DataOutputStream;
8 | import java.io.IOException;
9 | import java.io.InputStreamReader;
10 | import java.lang.reflect.Field;
11 | import java.util.List;
12 |
13 | import cn.myflv.android.noanr.entity.ClassEnum;
14 | import cn.myflv.android.noanr.entity.MethodEnum;
15 | import cn.myflv.android.noanr.utils.AnrUtil;
16 | import cn.myflv.android.noanr.utils.AppOpsUtil;
17 | import cn.myflv.android.noanr.utils.AppUtil;
18 | import cn.myflv.android.noanr.utils.BroadcastUtil;
19 | import cn.myflv.android.noanr.utils.ClassUtil;
20 | import cn.myflv.android.noanr.utils.ProcessUtil;
21 | import de.robv.android.xposed.IXposedHookLoadPackage;
22 | import de.robv.android.xposed.XC_MethodHook;
23 | import de.robv.android.xposed.XC_MethodReplacement;
24 | import de.robv.android.xposed.XposedBridge;
25 | import de.robv.android.xposed.XposedHelpers;
26 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
27 |
28 | public class Hook implements IXposedHookLoadPackage {
29 |
30 | private final static String NO_ANR = "NoANR";
31 | private final static boolean DEBUG = false;
32 |
33 | @Override
34 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
35 | ClassLoader classLoader = loadPackageParam.classLoader;
36 | if (loadPackageParam.packageName.equals("android")) {
37 | XposedBridge.log(NO_ANR + " Load success");
38 | XposedHelpers.findAndHookMethod(ClassEnum.BroadcastQueue, classLoader, MethodEnum.deliverToRegisteredReceiverLocked,
39 | ClassEnum.BroadcastRecord,
40 | ClassEnum.BroadcastFilter, boolean.class, int.class,
41 | new XC_MethodHook() {
42 | @Override
43 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
44 | super.beforeHookedMethod(param);
45 | Object broadcastQueue = param.thisObject;
46 | Object[] args = param.args;
47 | Object filter = args[1];
48 | Object receiverList = BroadcastUtil.getReceiverList(filter);
49 | if (receiverList == null) return;
50 | Object applicationInfo = BroadcastUtil.getApplicationInfo(receiverList);
51 | if (applicationInfo == null) return;
52 | String packageName = AppUtil.getPackageName(applicationInfo);
53 | if (packageName == null) return;
54 | Object packageManager = AppUtil.getPackageManager(classLoader);
55 | if (packageManager == null) return;
56 | if (AppUtil.isSystem(packageManager, packageName)) return;
57 | Object activityManagerService = BroadcastUtil.getActivityManagerService(broadcastQueue);
58 | if (activityManagerService == null) return;
59 | Object activeServices = AppUtil.getActiveServices(activityManagerService);
60 | if (activeServices == null) return;
61 | int uid = AppUtil.getUid(applicationInfo);
62 | Object appOpsManager = AppUtil.getAppOpsManager(activityManagerService, classLoader);
63 | if (appOpsManager == null) return;
64 | boolean wakeLockIgnore = AppOpsUtil.checkOpIgnore(appOpsManager, AppOpsUtil.OP_WAKE_LOCK, uid, packageName, classLoader);
65 | log(packageName + " -> " + wakeLockIgnore);
66 | if (!wakeLockIgnore) return;
67 | BroadcastUtil.clear(receiverList);
68 | log("Broadcast to " + packageName + " clean success");
69 | }
70 |
71 | }
72 | );
73 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
74 | XposedBridge.log(NO_ANR + " Auto keep process");
75 | XposedHelpers.findAndHookMethod(ClassEnum.AnrHelper, classLoader, MethodEnum.appNotResponding,
76 | ClassEnum.ProcessRecord,
77 | String.class,
78 | ClassEnum.ApplicationInfo,
79 | String.class,
80 | ClassEnum.WindowProcessController,
81 | boolean.class,
82 | String.class, new XC_MethodReplacement() {
83 | @Override
84 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
85 | Object[] args = param.args;
86 | Object anrHelper = param.thisObject;
87 | Object processRecord = args[0];
88 | if (processRecord == null) return null;
89 | Object applicationInfo = ProcessUtil.getApplicationInfo(processRecord);
90 | if (applicationInfo == null) return null;
91 | boolean isSystem = AppUtil.isSystem(applicationInfo);
92 | if (isSystem) {
93 | synchronized (AnrUtil.mAnrRecords(anrHelper)) {
94 | Object anrRecord = AnrUtil.newInstance(classLoader, args);
95 | AnrUtil.mAnrRecords(anrHelper).add(anrRecord);
96 | }
97 | AnrUtil.startAnrConsumerIfNeeded(anrHelper);
98 | } else {
99 | AnrUtil.resetNotResponding(processRecord);
100 | Object processName = XposedHelpers.getObjectField(processRecord, "processName");
101 | if (processName == null) processName = "unknown";
102 | log("Keep process " + processName + " success");
103 | }
104 | return null;
105 | }
106 | });
107 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
108 | XposedBridge.log(NO_ANR + " -> Android Q");
109 | XposedBridge.log(NO_ANR + " Force keep process");
110 | XposedHelpers.findAndHookMethod(ClassEnum.ProcessRecord, loadPackageParam.classLoader, MethodEnum.appNotResponding,
111 | String.class, ClassEnum.ApplicationInfo, String.class, ClassEnum.WindowProcessController, boolean.class, String.class, new XC_MethodReplacement() {
112 | @Override
113 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
114 | return null;
115 | }
116 | });
117 |
118 | } else {
119 | XposedBridge.log("NoANR -> Android N-P");
120 | XposedBridge.log(NO_ANR + " Force keep process");
121 | XposedHelpers.findAndHookMethod(ClassEnum.AppErrors, loadPackageParam.classLoader, MethodEnum.appNotResponding,
122 | ClassEnum.ProcessRecord, ClassEnum.ActivityRecord, ClassEnum.ActivityRecord, boolean.class, String.class,
123 | new XC_MethodReplacement() {
124 | @Override
125 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
126 | return null;
127 | }
128 | }
129 |
130 | );
131 | }
132 |
133 | }
134 | }
135 |
136 |
137 | public void log(String str) {
138 | if (DEBUG) XposedBridge.log(NO_ANR + " -> " + str);
139 | }
140 |
141 |
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noanr/entity/ClassEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noanr.entity;
2 |
3 | public class ClassEnum {
4 | public final static String ActivityManagerService = "com.android.server.am.ActivityManagerService";
5 | public final static String ComponentName = "android.content.ComponentName";
6 | public final static String IBinder = "android.os.IBinder";
7 | public final static String BroadcastQueue = "com.android.server.am.BroadcastQueue";
8 | public final static String BroadcastRecord = "com.android.server.am.BroadcastRecord";
9 | public final static String BroadcastFilter = "com.android.server.am.BroadcastFilter";
10 | public final static String AppGlobals = "android.app.AppGlobals";
11 | public final static String AppOpsManager = "android.app.AppOpsManager";
12 | public final static String AnrHelper = "com.android.server.am.AnrHelper";
13 | public final static String ProcessRecord = "com.android.server.am.ProcessRecord";
14 | public final static String ApplicationInfo = "android.content.pm.ApplicationInfo";
15 | public final static String WindowProcessController = "com.android.server.wm.WindowProcessController";
16 | public final static String AnrRecord = AnrHelper + "$AnrRecord";
17 | public final static String AppErrors = "com.android.server.am.AppErrors";
18 | public final static String ActivityRecord = "com.android.server.am.ActivityRecord";
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noanr/entity/FieldEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noanr.entity;
2 |
3 | public class FieldEnum {
4 | public final static String packageName = "packageName";
5 | public final static String uid = "uid";
6 | public final static String pid = "pid";
7 | public final static String flags = "flags";
8 | public final static String info = "info";
9 | public final static String mProcessList = "mProcessList";
10 | public final static String mLruProcesses = "mLruProcesses";
11 | public final static String mPackage = "mPackage";
12 | public final static String processName = "processName";
13 | public final static String mServices = "mServices";
14 | public final static String mService = "mService";
15 | public final static String receiverList = "receiverList";
16 | public final static String app = "app";
17 | public final static String mAnrRecords = "mAnrRecords";
18 | public final static String mContext = "mContext";
19 | public final static String mErrorState = "mErrorState";
20 | }
21 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noanr/entity/MethodEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noanr.entity;
2 |
3 | public class MethodEnum {
4 | public final static String updateActivityUsageStats = "updateActivityUsageStats";
5 | public final static String getAppOpsManager = "getAppOpsManager";
6 | public final static String getPackageManager = "getPackageManager";
7 | public final static String checkOpNoThrow = "checkOpNoThrow";
8 | public final static String sendSignal = "sendSignal";
9 | public final static String setMode = "setMode";
10 | public final static String deliverToRegisteredReceiverLocked = "deliverToRegisteredReceiverLocked";
11 | public final static String appRestrictedAnyInBackground = "appRestrictedAnyInBackground";
12 | public final static String appNotResponding = "appNotResponding";
13 | public final static String startAnrConsumerIfNeeded = "startAnrConsumerIfNeeded";
14 | public final static String getApplicationInfo = "getApplicationInfo";
15 | public final static String getSystemService = "getSystemService";
16 | public final static String mProcLock = "mProcLock";
17 | public final static String setNotResponding = "setNotResponding";
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noanr/utils/AnrUtil.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noanr.utils;
2 |
3 | import android.os.Build;
4 |
5 | import java.util.List;
6 |
7 | import cn.myflv.android.noanr.entity.ClassEnum;
8 | import cn.myflv.android.noanr.entity.FieldEnum;
9 | import cn.myflv.android.noanr.entity.MethodEnum;
10 | import de.robv.android.xposed.XposedHelpers;
11 |
12 | public class AnrUtil {
13 | public static List