├── .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
│ │ └── noactive
│ │ ├── Hook.java
│ │ ├── app
│ │ ├── Android.java
│ │ ├── IAppHook.java
│ │ └── PowerKeeper.java
│ │ ├── entity
│ │ ├── ClassEnum.java
│ │ ├── FieldEnum.java
│ │ ├── MemData.java
│ │ └── MethodEnum.java
│ │ ├── hook
│ │ ├── ANRHook.java
│ │ ├── AppSwitchHook.java
│ │ ├── BroadcastDeliverHook.java
│ │ ├── CacheFreezerHook.java
│ │ ├── MilletHook.java
│ │ ├── OomAdjHook.java
│ │ └── TestHook.java
│ │ ├── server
│ │ ├── ActiveServices.java
│ │ ├── ActivityManagerService.java
│ │ ├── AnrHelper.java
│ │ ├── ApplicationInfo.java
│ │ ├── BroadcastFilter.java
│ │ ├── BroadcastQueue.java
│ │ ├── ComponentName.java
│ │ ├── Event.java
│ │ ├── ProcessList.java
│ │ ├── ProcessRecord.java
│ │ ├── ProcessStateRecord.java
│ │ └── ReceiverList.java
│ │ └── utils
│ │ ├── ConfigFileObserver.java
│ │ ├── FreezeUtils.java
│ │ ├── FreezerConfig.java
│ │ ├── Log.java
│ │ └── ThreadUtil.java
│ └── res
│ ├── mipmap-anydpi-v26
│ ├── ic_logo.xml
│ └── ic_logo_round.xml
│ ├── mipmap-hdpi
│ ├── ic_logo.png
│ ├── ic_logo_foreground.png
│ └── ic_logo_round.png
│ ├── mipmap-mdpi
│ ├── ic_logo.png
│ ├── ic_logo_foreground.png
│ └── ic_logo_round.png
│ ├── mipmap-xhdpi
│ ├── ic_logo.png
│ ├── ic_logo_foreground.png
│ └── ic_logo_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_logo.png
│ ├── ic_logo_foreground.png
│ └── ic_logo_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_logo.png
│ ├── ic_logo_foreground.png
│ └── ic_logo_round.png
│ └── values
│ ├── array.xml
│ ├── color.xml
│ └── strings.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── 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 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | NoActive无图形版本
2 |
--------------------------------------------------------------------------------
/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.noactive"
10 | minSdk 23
11 | targetSdk 32
12 | versionCode 128
13 | versionName "0.9.6"
14 |
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | buildTypes {
19 | release {
20 | minifyEnabled false
21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
22 | }
23 | }
24 | compileOptions {
25 | sourceCompatibility JavaVersion.VERSION_1_8
26 | targetCompatibility JavaVersion.VERSION_1_8
27 | }
28 |
29 | android.applicationVariants.all {
30 | variant ->
31 | variant.outputs.all {
32 | //这里修改apk文件名
33 | outputFileName = "NoActive-v${variant.versionName}.apk"
34 | }
35 | }
36 | }
37 |
38 | dependencies {
39 | compileOnly 'org.projectlombok:lombok:1.18.24'
40 | annotationProcessor 'org.projectlombok:lombok:1.18.24'
41 | compileOnly 'de.robv.android.xposed:api:82'
42 | compileOnly 'de.robv.android.xposed:api:82:sources'
43 | }
--------------------------------------------------------------------------------
/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.noactive",
8 | "variantName": "release",
9 | "elements": [
10 | {
11 | "type": "SINGLE",
12 | "filters": [],
13 | "attributes": [],
14 | "versionCode": 127,
15 | "versionName": "0.9.6",
16 | "outputFile": "NoActive-v0.9.6.apk"
17 | }
18 | ],
19 | "elementType": "File"
20 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
13 |
16 |
17 |
20 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/assets/xposed_init:
--------------------------------------------------------------------------------
1 | cn.myflv.android.noactive.Hook
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/Hook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive;
2 |
3 | import cn.myflv.android.noactive.app.Android;
4 | import cn.myflv.android.noactive.app.PowerKeeper;
5 | import de.robv.android.xposed.IXposedHookLoadPackage;
6 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
7 |
8 | public class Hook implements IXposedHookLoadPackage {
9 |
10 | @Override
11 | public void handleLoadPackage(XC_LoadPackage.LoadPackageParam packageParam) throws Throwable {
12 | if (packageParam.packageName.equals("android")) {
13 | Android android = new Android();
14 | android.hook(packageParam);
15 | return;
16 | }
17 | // 禁用 millet
18 | if (packageParam.packageName.equals("com.miui.powerkeeper")) {
19 | PowerKeeper powerKeeper = new PowerKeeper();
20 | powerKeeper.hook(packageParam);
21 | }
22 |
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/app/Android.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.app;
2 |
3 | import android.content.Intent;
4 | import android.os.Build;
5 |
6 | import cn.myflv.android.noactive.entity.ClassEnum;
7 | import cn.myflv.android.noactive.entity.MemData;
8 | import cn.myflv.android.noactive.entity.MethodEnum;
9 | import cn.myflv.android.noactive.hook.ANRHook;
10 | import cn.myflv.android.noactive.hook.AppSwitchHook;
11 | import cn.myflv.android.noactive.hook.BroadcastDeliverHook;
12 | import cn.myflv.android.noactive.hook.CacheFreezerHook;
13 | import cn.myflv.android.noactive.hook.OomAdjHook;
14 | import cn.myflv.android.noactive.utils.FreezerConfig;
15 | import cn.myflv.android.noactive.utils.Log;
16 | import de.robv.android.xposed.XC_MethodReplacement;
17 | import de.robv.android.xposed.XposedHelpers;
18 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
19 |
20 | public class Android implements IAppHook {
21 | @Override
22 | public void hook(XC_LoadPackage.LoadPackageParam packageParam) {
23 | // 类加载器
24 | ClassLoader classLoader = packageParam.classLoader;
25 |
26 | // 加载内存配置
27 | MemData memData = new MemData();
28 |
29 | // 打印设备厂商
30 | Log.i(Build.MANUFACTURER + " device");
31 |
32 | // 禁用暂停执行已缓存
33 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
34 | XposedHelpers.findAndHookMethod(ClassEnum.CachedAppOptimizer, classLoader, MethodEnum.useFreezer, new CacheFreezerHook());
35 | Log.i("Disable cache freezer");
36 | }
37 |
38 | // Hook 切换事件
39 | if (Build.MANUFACTURER.equals("samsung")) {
40 | XposedHelpers.findAndHookMethod(ClassEnum.ActivityManagerService, classLoader,
41 | MethodEnum.updateActivityUsageStats,
42 | ClassEnum.ComponentName, int.class, int.class,
43 | ClassEnum.IBinder, ClassEnum.ComponentName, Intent.class, new AppSwitchHook(classLoader, memData, AppSwitchHook.DIFFICULT));
44 | } else {
45 | XposedHelpers.findAndHookMethod(ClassEnum.ActivityManagerService, classLoader,
46 | MethodEnum.updateActivityUsageStats,
47 | ClassEnum.ComponentName, int.class, int.class,
48 | ClassEnum.IBinder, ClassEnum.ComponentName, new AppSwitchHook(classLoader, memData, AppSwitchHook.DIFFICULT));
49 | }
50 |
51 | // Hook 广播分发
52 | XposedHelpers.findAndHookMethod(ClassEnum.BroadcastQueue, classLoader, MethodEnum.deliverToRegisteredReceiverLocked,
53 | ClassEnum.BroadcastRecord,
54 | ClassEnum.BroadcastFilter, boolean.class, int.class, new BroadcastDeliverHook(memData));
55 |
56 | // Hook oom_adj
57 | if (!FreezerConfig.isConfigOn(FreezerConfig.disableOOM)) {
58 | boolean colorOs = FreezerConfig.isColorOs();
59 | if (!colorOs && (Build.MANUFACTURER.equals("OPPO") || Build.MANUFACTURER.equals("OnePlus"))) {
60 | Log.w("If you are using ColorOS");
61 | Log.w("You can create file color.os");
62 | }
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
64 | if (colorOs) {
65 | Log.i("Hello ColorOS");
66 | XposedHelpers.findAndHookMethod(ClassEnum.OomAdjuster, classLoader, MethodEnum.computeOomAdjLSP, ClassEnum.ProcessRecord, int.class, ClassEnum.ProcessRecord, boolean.class, long.class, boolean.class, boolean.class, new OomAdjHook(classLoader, memData, OomAdjHook.Color));
67 | } else {
68 | XposedHelpers.findAndHookMethod(ClassEnum.ProcessStateRecord, classLoader, MethodEnum.setCurAdj, int.class, new OomAdjHook(classLoader, memData, OomAdjHook.Android_S));
69 | }
70 | Log.i("Auto lmk");
71 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R || Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
72 | XposedHelpers.findAndHookMethod(ClassEnum.OomAdjuster, classLoader, MethodEnum.applyOomAdjLocked, ClassEnum.ProcessRecord, boolean.class, long.class, long.class, new OomAdjHook(classLoader, memData, OomAdjHook.Android_Q_R));
73 | Log.i("Auto lmk");
74 | }
75 | }
76 |
77 | // Hook ANR
78 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
79 | XposedHelpers.findAndHookMethod(ClassEnum.AnrHelper, classLoader, MethodEnum.appNotResponding,
80 | ClassEnum.ProcessRecord,
81 | String.class,
82 | ClassEnum.ApplicationInfo,
83 | String.class,
84 | ClassEnum.WindowProcessController,
85 | boolean.class,
86 | String.class, new ANRHook(classLoader, memData));
87 | Log.i("Auto keep process");
88 | } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
89 | XposedHelpers.findAndHookMethod(ClassEnum.ProcessRecord, classLoader, MethodEnum.appNotResponding,
90 | String.class, ClassEnum.ApplicationInfo, String.class, ClassEnum.WindowProcessController, boolean.class, String.class, XC_MethodReplacement.DO_NOTHING);
91 | Log.i("Android Q");
92 | Log.i("Force keep process");
93 | } else {
94 | XposedHelpers.findAndHookMethod(ClassEnum.AppErrors, classLoader, MethodEnum.appNotResponding,
95 | ClassEnum.ProcessRecord, ClassEnum.ActivityRecord, ClassEnum.ActivityRecord, boolean.class, String.class,
96 | XC_MethodReplacement.DO_NOTHING
97 | );
98 | Log.i("Android N-P");
99 | Log.i("Force keep process");
100 | }
101 |
102 | Log.i("Load success");
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/app/IAppHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.app;
2 |
3 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
4 |
5 | public interface IAppHook {
6 | void hook(XC_LoadPackage.LoadPackageParam packageParam);
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/app/PowerKeeper.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.app;
2 |
3 | import android.content.Context;
4 |
5 | import cn.myflv.android.noactive.entity.ClassEnum;
6 | import cn.myflv.android.noactive.entity.MethodEnum;
7 | import cn.myflv.android.noactive.hook.MilletHook;
8 | import de.robv.android.xposed.XC_MethodReplacement;
9 | import de.robv.android.xposed.XposedBridge;
10 | import de.robv.android.xposed.XposedHelpers;
11 | import de.robv.android.xposed.callbacks.XC_LoadPackage;
12 |
13 | public class PowerKeeper implements IAppHook {
14 | @Override
15 | public void hook(XC_LoadPackage.LoadPackageParam packageParam) {
16 | try {
17 | XposedHelpers.findAndHookMethod(ClassEnum.PowerStateMachine, packageParam.classLoader, MethodEnum.clearAppWhenScreenOffTimeOut, XC_MethodReplacement.DO_NOTHING);
18 | XposedHelpers.findAndHookMethod(ClassEnum.PowerStateMachine, packageParam.classLoader, MethodEnum.clearAppWhenScreenOffTimeOutInNight, XC_MethodReplacement.DO_NOTHING);
19 | XposedHelpers.findAndHookMethod(ClassEnum.PowerStateMachine, packageParam.classLoader, MethodEnum.clearUnactiveApps, Context.class, XC_MethodReplacement.DO_NOTHING);
20 | XposedBridge.log("NoActive(info) -> Disable MIUI clearApp");
21 | } catch (Throwable throwable) {
22 | XposedBridge.log("NoActive(error) -> Disable MIUI clearApp failed: " + throwable.getMessage());
23 | }
24 |
25 | try {
26 | XposedHelpers.findAndHookMethod(ClassEnum.MilletConfig, packageParam.classLoader, MethodEnum.getEnable, Context.class, new MilletHook());
27 | XposedBridge.log("NoActive(info) -> Disable millet");
28 | } catch (Throwable throwable) {
29 | XposedBridge.log("NoActive(error) -> Disable millet failed: " + throwable.getMessage());
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/entity/ClassEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.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 AnrHelper = "com.android.server.am.AnrHelper";
11 | public final static String ProcessRecord = "com.android.server.am.ProcessRecord";
12 | public final static String ApplicationInfo = "android.content.pm.ApplicationInfo";
13 | public final static String WindowProcessController = "com.android.server.wm.WindowProcessController";
14 | public final static String AnrRecord = AnrHelper + "$AnrRecord";
15 | public final static String AppErrors = "com.android.server.am.AppErrors";
16 | public final static String ActivityRecord = "com.android.server.am.ActivityRecord";
17 |
18 |
19 | public final static String ProcessStateRecord = "com.android.server.am.ProcessStateRecord";
20 | public final static String OomAdjuster = "com.android.server.am.OomAdjuster";
21 |
22 | public final static String MilletConfig = "com.miui.powerkeeper.millet.MilletConfig";
23 | public final static String CachedAppOptimizer = "com.android.server.am.CachedAppOptimizer";
24 | public final static String ProcessList = "com.android.server.am.ProcessList";
25 | public final static String PowerStateMachine = "com.miui.powerkeeper.statemachine.PowerStateMachine";
26 | public final static String Process = "android.os.Process";
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/entity/FieldEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.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 static String mPid = "mPid";
14 |
15 |
16 | public final static String mServices = "mServices";
17 | public final static String mService = "mService";
18 | public final static String receiverList = "receiverList";
19 | public final static String app = "app";
20 | public final static String mAnrRecords = "mAnrRecords";
21 | public final static String mContext = "mContext";
22 | public final static String userId = "userId";
23 | public final static String mApp = "mApp";
24 | public final static String curAdj = "curAdj";
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/entity/MemData.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.entity;
2 |
3 | import android.os.FileObserver;
4 |
5 | import java.util.Collection;
6 | import java.util.Collections;
7 | import java.util.HashSet;
8 | import java.util.LinkedHashSet;
9 | import java.util.Set;
10 |
11 | import cn.myflv.android.noactive.utils.ConfigFileObserver;
12 | import lombok.Data;
13 |
14 | @Data
15 | public class MemData {
16 | private Set whiteApps = new HashSet<>();
17 | private Set blackSystemApps = new HashSet<>();
18 | private Set whiteProcessList = new HashSet<>();
19 | private Set killProcessList = new HashSet<>();
20 | private Set appBackgroundSet = Collections.synchronizedSet(new LinkedHashSet<>());
21 | private final FileObserver fileObserver = new ConfigFileObserver(this);
22 |
23 | public MemData() {
24 | fileObserver.startWatching();
25 | }
26 |
27 | public int getBackgroundIndex(String packageName) {
28 | int total = appBackgroundSet.size();
29 | for (String pkg : appBackgroundSet) {
30 | if (whiteApps.contains(pkg)) {
31 | continue;
32 | }
33 | if (packageName.equals(pkg)) {
34 | return total;
35 | } else {
36 | total -= 1;
37 | }
38 | }
39 | return total;
40 | }
41 |
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/entity/MethodEnum.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.entity;
2 |
3 | public class MethodEnum {
4 | public final static String updateActivityUsageStats = "updateActivityUsageStats";
5 |
6 |
7 | public final static String deliverToRegisteredReceiverLocked = "deliverToRegisteredReceiverLocked";
8 | public final static String appNotResponding = "appNotResponding";
9 | public final static String startAnrConsumerIfNeeded = "startAnrConsumerIfNeeded";
10 | public final static String isAppForeground = "isAppForeground";
11 | public final static String add = "add";
12 | public final static String killServicesLocked = "killServicesLocked";
13 | public final static String setCurAdj = "setCurAdj";
14 |
15 | public final static String applyOomAdjLocked = "applyOomAdjLocked";
16 | public final static String getEnable = "getEnable";
17 | public final static String isFreezerSupported = "isFreezerSupported";
18 | public final static String setProcessFrozen = "setProcessFrozen";
19 | public final static String useFreezer = "useFreezer";
20 | public final static String computeOomAdjLSP = "computeOomAdjLSP";
21 | public final static String setOomAdj = "setOomAdj";
22 | public final static String clearAppWhenScreenOffTimeOutInNight = "clearAppWhenScreenOffTimeOutInNight";
23 | public final static String clearAppWhenScreenOffTimeOut = "clearAppWhenScreenOffTimeOut";
24 | public final static String clearUnactiveApps = "clearUnactiveApps";
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/ANRHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import cn.myflv.android.noactive.entity.MemData;
4 | import cn.myflv.android.noactive.server.ActivityManagerService;
5 | import cn.myflv.android.noactive.server.AnrHelper;
6 | import cn.myflv.android.noactive.server.ProcessRecord;
7 | import cn.myflv.android.noactive.utils.Log;
8 | import de.robv.android.xposed.XC_MethodReplacement;
9 |
10 | public class ANRHook extends XC_MethodReplacement {
11 | private final ClassLoader classLoader;
12 | private final MemData memData;
13 |
14 | public ANRHook(ClassLoader classLoader, MemData memData) {
15 | this.classLoader = classLoader;
16 | this.memData = memData;
17 | }
18 |
19 | @Override
20 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
21 | // 获取方法参数
22 | Object[] args = param.args;
23 | AnrHelper anrHelper = new AnrHelper(classLoader, param.thisObject);
24 | // ANR进程为空就不处理
25 | if (args[0] == null) return null;
26 | // ANR进程
27 | ProcessRecord processRecord = new ProcessRecord(args[0]);
28 | // 是否系统进程
29 | boolean isSystem = processRecord.getApplicationInfo().isSystem();
30 | // 进程对应包名
31 | String packageName = processRecord.getApplicationInfo().getPackageName();
32 | boolean isNotBlackSystem = !memData.getBlackSystemApps().contains(packageName);
33 |
34 | // 系统应用并且不是系统黑名单
35 | if ((isSystem && isNotBlackSystem) || processRecord.getUserId() != ActivityManagerService.MAIN_USER) {
36 | synchronized (anrHelper.getAnrRecords()) {
37 | // 添加ANR记录
38 | anrHelper.add(args);
39 | }
40 | // 开启ANR消费如果需要
41 | anrHelper.startAnrConsumerIfNeeded();
42 | }
43 | Log.d("Keep " + (processRecord.getProcessName() != null ? processRecord.getProcessName() : packageName));
44 | // 不处理
45 | return null;
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/AppSwitchHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Collections;
5 | import java.util.HashMap;
6 | import java.util.List;
7 | import java.util.Map;
8 |
9 | import cn.myflv.android.noactive.entity.MemData;
10 | import cn.myflv.android.noactive.server.ActivityManagerService;
11 | import cn.myflv.android.noactive.server.ApplicationInfo;
12 | import cn.myflv.android.noactive.server.ComponentName;
13 | import cn.myflv.android.noactive.server.Event;
14 | import cn.myflv.android.noactive.server.ProcessList;
15 | import cn.myflv.android.noactive.server.ProcessRecord;
16 | import cn.myflv.android.noactive.utils.FreezeUtils;
17 | import cn.myflv.android.noactive.utils.Log;
18 | import cn.myflv.android.noactive.utils.ThreadUtil;
19 | import de.robv.android.xposed.XC_MethodHook;
20 |
21 | public class AppSwitchHook extends XC_MethodHook {
22 | private final int ACTIVITY_RESUMED;
23 | private final int ACTIVITY_PAUSED;
24 |
25 | public final static int SIMPLE = 1;
26 | public final static int DIFFICULT = 2;
27 | private final int type;
28 |
29 | private final MemData memData;
30 | private final FreezeUtils freezeUtils;
31 | private final Map freezerTokenMap = Collections.synchronizedMap(new HashMap<>());
32 |
33 | public AppSwitchHook(ClassLoader classLoader, MemData memData, int type) {
34 | this.ACTIVITY_RESUMED = Event.ACTIVITY_RESUMED(classLoader);
35 | this.ACTIVITY_PAUSED = Event.ACTIVITY_PAUSED(classLoader);
36 | this.memData = memData;
37 | this.freezeUtils = new FreezeUtils(classLoader);
38 | this.type = type;
39 | }
40 |
41 | /**
42 | * Hook APP 切换事件
43 | *
44 | * @param param 方法参数
45 | * @throws Throwable 异常
46 | */
47 | public void beforeHookedMethod(MethodHookParam param) throws Throwable {
48 | // 开启一个新线程防止避免阻塞主线程
49 | new Thread(() -> {
50 | // 获取方法参数
51 | Object[] args = param.args;
52 | // 获取切换事件
53 | int event = (int) args[2];
54 | // AMS有两个方法,但参数不同
55 | String packageName = type == SIMPLE ? (String) args[0] : new ComponentName(args[0]).getPackageName();
56 | int userId = (int) args[1];
57 | if (userId != ActivityManagerService.MAIN_USER) {
58 | return;
59 | }
60 | // 如果是进入前台
61 | if (event == ACTIVITY_RESUMED) {
62 | // 后台APP移除
63 | memData.getAppBackgroundSet().remove(packageName);
64 | } else if (event != ACTIVITY_PAUSED) {
65 | // 不是进入前台或者后台就不处理
66 | return;
67 | }
68 |
69 | // 获取AMS
70 | ActivityManagerService activityManagerService = new ActivityManagerService(param.thisObject);
71 | // 重要系统APP
72 | boolean isImportantSystemApp = activityManagerService.isImportantSystemApp(packageName);
73 | if (isImportantSystemApp) {
74 | Log.d(packageName + " is important system app");
75 | return;
76 | }
77 | // 系统APP
78 | boolean isSystem = activityManagerService.isSystem(packageName);
79 | // 判断是否白名单系统APP
80 | if (isSystem && !memData.getBlackSystemApps().contains(packageName)) {
81 | Log.d(packageName + " is white system app");
82 | return;
83 | }
84 | if (event == ACTIVITY_PAUSED) {
85 | //暂停事件
86 | onPause(activityManagerService, packageName);
87 | } else {
88 | //继续事件
89 | onResume(activityManagerService, packageName);
90 | }
91 | }).start();
92 | }
93 |
94 | /**
95 | * 获取目标进程
96 | *
97 | * @param activityManagerService AMS
98 | * @param packageName 包名
99 | * @return 目标进程列表
100 | */
101 | public List getTargetProcessRecords(ActivityManagerService activityManagerService, String packageName) {
102 | // 从AMS获取进程列表对象
103 | ProcessList processList = activityManagerService.getProcessList();
104 | // 从进程列表对象获取所有进程
105 | List processRecords = processList.getProcessRecords();
106 | // 存放需要冻结/解冻的 processRecord
107 | List targetProcessRecords = new ArrayList<>();
108 | // 对进程列表加锁
109 | synchronized (processList.getProcessList()) {
110 | // 遍历进程列表
111 | for (ProcessRecord processRecord : processRecords) {
112 | if (processRecord.getUserId() != ActivityManagerService.MAIN_USER) {
113 | continue;
114 | }
115 | ApplicationInfo applicationInfo = processRecord.getApplicationInfo();
116 | // 如果包名和事件的包名不同就不处理
117 | if (!applicationInfo.getPackageName().equals(packageName)) {
118 | continue;
119 | }
120 | // 获取进程名
121 | String processName = processRecord.getProcessName();
122 | // 如果进程名称不是包名开头就跳过
123 | if (!processName.startsWith(packageName)) {
124 | continue;
125 | }
126 | // 如果白名单进程包含进程则跳过
127 | if (memData.getWhiteProcessList().contains(processName)) {
128 | Log.d("white process " + processName);
129 | continue;
130 | }
131 | // 如果白名单APP包含包名并且杀死进程不包含进程名就跳过
132 | if (memData.getWhiteApps().contains(packageName) && !memData.getKillProcessList().contains(processName)) {
133 | Log.d("white app process " + processName);
134 | continue;
135 | }
136 | // 添加目标进程
137 | targetProcessRecords.add(processRecord);
138 | }
139 | }
140 | return targetProcessRecords;
141 | }
142 |
143 | /**
144 | * APP切换至前台
145 | *
146 | * @param packageName 包名
147 | * @param activityManagerService AMS
148 | */
149 | public void onResume(ActivityManagerService activityManagerService, String packageName) {
150 | List targetProcessRecords = getTargetProcessRecords(activityManagerService, packageName);
151 | // 如果目标进程为空就不处理
152 | if (targetProcessRecords.isEmpty()) {
153 | return;
154 | }
155 | Log.d(packageName + " resumed");
156 | // 遍历目标进程列表
157 | for (ProcessRecord targetProcessRecord : targetProcessRecords) {
158 | // 确保APP不在后台
159 | if (memData.getAppBackgroundSet().contains(packageName)) {
160 | return;
161 | }
162 | // 解冻进程
163 | freezeUtils.unFreezer(targetProcessRecord);
164 | }
165 | }
166 |
167 | /**
168 | * APP切换至后台
169 | *
170 | * @param activityManagerService AMS
171 | * @param packageName 包名
172 | */
173 | public void onPause(ActivityManagerService activityManagerService, String packageName) {
174 | Log.d(packageName + " paused");
175 | long token = System.currentTimeMillis();
176 | setToken(packageName, token);
177 | // 休眠3s
178 | ThreadUtil.sleep(3000);
179 | if (!isCorrectToken(packageName, token)) {
180 | return;
181 | }
182 |
183 | // 应用是否前台
184 | boolean isAppForeground = activityManagerService.isAppForeground(packageName);
185 | // 如果是前台应用就不处理
186 | if (isAppForeground) {
187 | Log.d(packageName + " is in foreground");
188 | return;
189 | }
190 |
191 | // 后台应用添加包名
192 | memData.getAppBackgroundSet().add(packageName);
193 |
194 | List targetProcessRecords = getTargetProcessRecords(activityManagerService, packageName);
195 | // 如果目标进程为空就不处理
196 | if (targetProcessRecords.isEmpty()) {
197 | return;
198 | }
199 |
200 | // 遍历目标进程
201 | for (ProcessRecord targetProcessRecord : targetProcessRecords) {
202 | // 应用又进入前台了
203 | if (!memData.getAppBackgroundSet().contains(packageName)) {
204 | // 为保证解冻顺利
205 | return;
206 | }
207 | // 目标进程名
208 | String processName = targetProcessRecord.getProcessName();
209 | // 目标进程PID
210 | int pid = targetProcessRecord.getPid();
211 | // 如果杀死进程列表包含进程名
212 | if (memData.getKillProcessList().contains(processName)) {
213 | Log.d(processName + " kill");
214 | // 杀死进程
215 | FreezeUtils.kill(pid);
216 | } else {
217 | Log.d(processName + " freezer");
218 | freezeUtils.freezer(targetProcessRecord);
219 | }
220 | }
221 | }
222 |
223 |
224 | public void setToken(String packageName, long token) {
225 | freezerTokenMap.put(packageName, token);
226 | }
227 |
228 | public boolean isCorrectToken(String packageName, long value) {
229 | Long token = freezerTokenMap.get(packageName);
230 | return token != null && value == token;
231 | }
232 |
233 | }
234 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/BroadcastDeliverHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import cn.myflv.android.noactive.entity.MemData;
5 | import cn.myflv.android.noactive.server.ActivityManagerService;
6 | import cn.myflv.android.noactive.server.ApplicationInfo;
7 | import cn.myflv.android.noactive.server.BroadcastFilter;
8 | import cn.myflv.android.noactive.server.ProcessRecord;
9 | import cn.myflv.android.noactive.server.ReceiverList;
10 | import cn.myflv.android.noactive.utils.Log;
11 | import de.robv.android.xposed.XC_MethodHook;
12 | import de.robv.android.xposed.XposedHelpers;
13 |
14 | public class BroadcastDeliverHook extends XC_MethodHook {
15 | private final MemData memData;
16 |
17 | public BroadcastDeliverHook(MemData memData) {
18 | this.memData = memData;
19 | }
20 |
21 | @Override
22 | public void beforeHookedMethod(MethodHookParam param) throws Throwable {
23 | Object[] args = param.args;
24 | if (args[1] == null) {
25 | return;
26 | }
27 | BroadcastFilter broadcastFilter = new BroadcastFilter(args[1]);
28 | ReceiverList receiverList = broadcastFilter.getReceiverList();
29 | // 如果广播为空就不处理
30 | if (receiverList == null) {
31 | return;
32 | }
33 | ProcessRecord processRecord = receiverList.getProcessRecord();
34 | // 如果进程或者应用信息为空就不处理
35 | if (processRecord == null || processRecord.getApplicationInfo() == null) {
36 | return;
37 | }
38 | if (processRecord.getUserId() != ActivityManagerService.MAIN_USER) {
39 | return;
40 | }
41 | ApplicationInfo applicationInfo = processRecord.getApplicationInfo();
42 | String packageName = processRecord.getApplicationInfo().getPackageName();
43 | // 如果包名为空就不处理(猜测系统进程可能为空)
44 | if (packageName == null) {
45 | return;
46 | }
47 | String processName = processRecord.getProcessName();
48 | // 如果进程名称不是包名开头就跳过
49 | if (!processName.startsWith(packageName)) {
50 | return;
51 | }
52 | // 如果是系统应用并且不是系统黑名单就不处理
53 | if (applicationInfo.getUid() < 10000 || (applicationInfo.isSystem() && !memData.getBlackSystemApps().contains(packageName))) {
54 | return;
55 | }
56 | // 如果是前台应用就不处理
57 | if (!memData.getAppBackgroundSet().contains(packageName)) {
58 | return;
59 | }
60 | // 如果白名单应用或者进程就不处理
61 | if (memData.getWhiteApps().contains(packageName) || memData.getWhiteProcessList().contains(processName)) {
62 | return;
63 | }
64 | // 暂存
65 | Object app = processRecord.getProcessRecord();
66 | param.setObjectExtra(FieldEnum.app, app);
67 | Log.d(processRecord.getProcessName() + " clear broadcast");
68 | // 清楚广播
69 | receiverList.clear();
70 | }
71 |
72 | @Override
73 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
74 | super.afterHookedMethod(param);
75 |
76 | // 获取进程
77 | Object app = param.getObjectExtra(FieldEnum.app);
78 | if (app == null) {
79 | return;
80 | }
81 |
82 | Object[] args = param.args;
83 | if (args[1] == null) {
84 | return;
85 | }
86 | Object receiverList = XposedHelpers.getObjectField(args[1], FieldEnum.receiverList);
87 | if (receiverList == null) {
88 | return;
89 | }
90 | // 还原修改
91 | XposedHelpers.setObjectField(receiverList, FieldEnum.app, app);
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/CacheFreezerHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import de.robv.android.xposed.XC_MethodReplacement;
4 |
5 | public class CacheFreezerHook extends XC_MethodReplacement {
6 | @Override
7 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
8 | return false;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/MilletHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import de.robv.android.xposed.XC_MethodReplacement;
4 |
5 | public class MilletHook extends XC_MethodReplacement {
6 | @Override
7 | protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {
8 | return false;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/OomAdjHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import cn.myflv.android.noactive.entity.MemData;
4 | import cn.myflv.android.noactive.server.ActivityManagerService;
5 | import cn.myflv.android.noactive.server.ApplicationInfo;
6 | import cn.myflv.android.noactive.server.ProcessList;
7 | import cn.myflv.android.noactive.server.ProcessRecord;
8 | import cn.myflv.android.noactive.server.ProcessStateRecord;
9 | import cn.myflv.android.noactive.utils.Log;
10 | import de.robv.android.xposed.XC_MethodHook;
11 |
12 | public class OomAdjHook extends XC_MethodHook {
13 | private final ClassLoader classLoader;
14 | private final MemData memData;
15 | private final int type;
16 | public final static int Android_S = 1;
17 | public final static int Android_Q_R = 2;
18 | public final static int Color = 3;
19 |
20 | public OomAdjHook(ClassLoader classLoader, MemData memData, int type) {
21 | this.classLoader = classLoader;
22 | this.memData = memData;
23 | this.type = type;
24 | }
25 |
26 |
27 | public void computeOomAdj(MethodHookParam param) {
28 | ProcessRecord processRecord;
29 | switch (type) {
30 | case Android_S:
31 | processRecord = new ProcessStateRecord(param.thisObject).getProcessRecord();
32 | break;
33 | case Android_Q_R:
34 | case Color:
35 | processRecord = new ProcessRecord(param.args[0]);
36 | break;
37 | default:
38 | return;
39 | }
40 | // 如果进程或者应用信息为空就不处理
41 | if (processRecord == null || processRecord.getApplicationInfo() == null) {
42 | return;
43 | }
44 | if (processRecord.getUserId()!= ActivityManagerService.MAIN_USER){
45 | return;
46 | }
47 | ApplicationInfo applicationInfo = processRecord.getApplicationInfo();
48 | String packageName = processRecord.getApplicationInfo().getPackageName();
49 | // 如果包名为空就不处理(猜测系统进程可能为空)
50 | if (packageName == null) {
51 | return;
52 | }
53 | String processName = processRecord.getProcessName();
54 | // 如果进程名称等于包名就跳过
55 | if (!processName.startsWith(packageName)) {
56 | return;
57 | }
58 | // 如果是系统应用并且不是系统黑名单就不处理
59 | if (applicationInfo.getUid() < 10000 || (applicationInfo.isSystem() && !memData.getBlackSystemApps().contains(packageName))) {
60 | return;
61 | }
62 | // 如果是前台应用就不处理
63 | if (!memData.getAppBackgroundSet().contains(packageName)) {
64 | return;
65 | }
66 | int finalCurlAdj;
67 |
68 | // 如果白名单应用或者进程就不处理
69 | if (memData.getWhiteApps().contains(packageName) || memData.getWhiteProcessList().contains(processName)) {
70 | finalCurlAdj = processName.equals(packageName) ? 500 : 700;
71 | } else {
72 | int curAdj = processName.equals(packageName) ? 700 : 900;
73 | finalCurlAdj = curAdj + memData.getBackgroundIndex(packageName);
74 | }
75 |
76 | Log.d(processName + " -> " + finalCurlAdj);
77 |
78 |
79 | switch (type) {
80 | case Android_S:
81 | param.args[0] = finalCurlAdj;
82 | break;
83 | case Android_Q_R:
84 | processRecord.setCurAdj(finalCurlAdj);
85 | break;
86 | case Color:
87 | ProcessList.setOomAdj(classLoader, processRecord.getPid(), processRecord.getUid(), finalCurlAdj);
88 | break;
89 | default:
90 | }
91 |
92 | }
93 |
94 | @Override
95 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
96 | super.beforeHookedMethod(param);
97 | if (type != Color) {
98 | computeOomAdj(param);
99 | }
100 | }
101 |
102 | @Override
103 | protected void afterHookedMethod(MethodHookParam param) throws Throwable {
104 | super.afterHookedMethod(param);
105 | if (type == Color) {
106 | computeOomAdj(param);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/hook/TestHook.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.hook;
2 |
3 | import android.os.Process;
4 |
5 | import cn.myflv.android.noactive.server.ProcessRecord;
6 | import cn.myflv.android.noactive.utils.Log;
7 | import de.robv.android.xposed.XC_MethodHook;
8 |
9 | public class TestHook extends XC_MethodHook {
10 | private final String TAG;
11 |
12 | public TestHook(String TAG) {
13 | this.TAG = TAG;
14 | }
15 |
16 | @Override
17 | protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
18 | super.beforeHookedMethod(param);
19 | Log.d(TAG);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ActiveServices.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import cn.myflv.android.noactive.entity.MethodEnum;
5 | import de.robv.android.xposed.XposedHelpers;
6 | import lombok.Data;
7 |
8 | @Data
9 | public class ActiveServices {
10 | private final Object activeServices;
11 |
12 | public ActiveServices(Object activeServices) {
13 | this.activeServices = activeServices;
14 | }
15 |
16 | public void killServicesLocked(ProcessRecord processRecord) {
17 | XposedHelpers.callMethod(activeServices, MethodEnum.killServicesLocked, processRecord.getProcessRecord(), false);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ActivityManagerService.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 |
4 | import android.content.Context;
5 | import android.content.pm.ApplicationInfo;
6 | import android.content.pm.PackageManager;
7 |
8 | import java.lang.reflect.InvocationTargetException;
9 |
10 | import cn.myflv.android.noactive.entity.ClassEnum;
11 | import cn.myflv.android.noactive.entity.FieldEnum;
12 | import cn.myflv.android.noactive.entity.MethodEnum;
13 | import cn.myflv.android.noactive.utils.Log;
14 | import de.robv.android.xposed.XposedHelpers;
15 | import lombok.Data;
16 |
17 | @Data
18 | public class ActivityManagerService {
19 | public final static int MAIN_USER = 0;
20 | private final Object activityManagerService;
21 | private final ProcessList processList;
22 | private final ActiveServices activeServices;
23 | private final Context context;
24 |
25 | public ActivityManagerService(Object activityManagerService) {
26 | this.activityManagerService = activityManagerService;
27 | this.processList = new ProcessList(XposedHelpers.getObjectField(activityManagerService, FieldEnum.mProcessList));
28 | this.activeServices = new ActiveServices(XposedHelpers.getObjectField(activityManagerService, FieldEnum.mServices));
29 | this.context = (Context) XposedHelpers.getObjectField(activityManagerService, FieldEnum.mContext);
30 | }
31 |
32 | public boolean isAppForeground(String packageName) {
33 | ApplicationInfo applicationInfo = getApplicationInfo(packageName);
34 | if (applicationInfo == null) {
35 | return true;
36 | }
37 | int uid = applicationInfo.uid;
38 | Class> clazz = activityManagerService.getClass();
39 | while (clazz != null && !clazz.getName().equals(Object.class.getName()) && !clazz.getName().equals(ClassEnum.ActivityManagerService)) {
40 | clazz = clazz.getSuperclass();
41 | }
42 | if (clazz == null || !clazz.getName().equals(ClassEnum.ActivityManagerService)) {
43 | Log.d("super activityManagerService is not found");
44 | return true;
45 | }
46 | try {
47 | return (boolean) XposedHelpers.findMethodBestMatch(clazz, MethodEnum.isAppForeground, uid).invoke(activityManagerService, uid);
48 | } catch (IllegalAccessException | InvocationTargetException e) {
49 | Log.d("call isAppForeground method error");
50 | }
51 | return true;
52 | }
53 |
54 | public boolean isSystem(String packageName) {
55 | ApplicationInfo applicationInfo = getApplicationInfo(packageName);
56 | if (applicationInfo == null) {
57 | return true;
58 | }
59 | return (applicationInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
60 | }
61 |
62 | public boolean isImportantSystemApp(String packageName) {
63 | ApplicationInfo applicationInfo = getApplicationInfo(packageName);
64 | if (applicationInfo == null) {
65 | return true;
66 | }
67 | return applicationInfo.uid < 10000;
68 | }
69 |
70 | public ApplicationInfo getApplicationInfo(String packageName) {
71 | try {
72 | PackageManager packageManager = context.getPackageManager();
73 | return packageManager.getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
74 | } catch (PackageManager.NameNotFoundException e) {
75 | Log.d(packageName + " not found");
76 | }
77 | return null;
78 | }
79 |
80 |
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/AnrHelper.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.ClassEnum;
4 | import cn.myflv.android.noactive.entity.FieldEnum;
5 | import cn.myflv.android.noactive.entity.MethodEnum;
6 | import de.robv.android.xposed.XposedHelpers;
7 | import lombok.Data;
8 |
9 | @Data
10 | public class AnrHelper {
11 | private final ClassLoader classLoader;
12 | private final Object anrHelper;
13 | private final Object anrRecords;
14 | // private final ActivityManagerService activityManagerService;
15 |
16 | public AnrHelper(ClassLoader classLoader, Object anrHelper) {
17 | this.classLoader = classLoader;
18 | this.anrHelper = anrHelper;
19 | this.anrRecords = XposedHelpers.getObjectField(anrHelper, FieldEnum.mAnrRecords);
20 | // this.activityManagerService = new ActivityManagerService(XposedHelpers.getObjectField(anrHelper, FieldEnum.mService));
21 | }
22 |
23 |
24 | public void startAnrConsumerIfNeeded() {
25 | XposedHelpers.callMethod(anrHelper, MethodEnum.startAnrConsumerIfNeeded);
26 | }
27 |
28 | public void add(Object... args) {
29 | Class> AnrRecord = XposedHelpers.findClass(ClassEnum.AnrRecord, classLoader);
30 | Object anrRecord = XposedHelpers.newInstance(AnrRecord, args);
31 | XposedHelpers.callMethod(anrRecords, MethodEnum.add, anrRecord);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ApplicationInfo.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import cn.myflv.android.noactive.utils.Log;
5 | import de.robv.android.xposed.XposedHelpers;
6 | import lombok.Data;
7 |
8 | @Data
9 | public class ApplicationInfo {
10 | private final int FLAG_SYSTEM;
11 | private final int FLAG_UPDATED_SYSTEM_APP;
12 | private final int flags;
13 | private final int uid;
14 | private final String processName;
15 | private final String packageName;
16 | private Object applicationInfo;
17 |
18 | public ApplicationInfo(Object applicationInfo) {
19 | this.applicationInfo = applicationInfo;
20 | this.processName = (String) XposedHelpers.getObjectField(applicationInfo, FieldEnum.processName);
21 | this.packageName = (String) XposedHelpers.getObjectField(applicationInfo, FieldEnum.packageName);
22 | this.flags = XposedHelpers.getIntField(applicationInfo, FieldEnum.flags);
23 | this.uid = XposedHelpers.getIntField(applicationInfo, FieldEnum.uid);
24 | this.FLAG_SYSTEM = XposedHelpers.getStaticIntField(applicationInfo.getClass(), "FLAG_SYSTEM");
25 | this.FLAG_UPDATED_SYSTEM_APP = XposedHelpers.getStaticIntField(applicationInfo.getClass(), "FLAG_UPDATED_SYSTEM_APP");
26 | }
27 |
28 |
29 | public boolean isSystem() {
30 | return (flags & (FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP)) != 0;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/BroadcastFilter.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import de.robv.android.xposed.XposedHelpers;
5 | import lombok.Data;
6 |
7 | @Data
8 | public class BroadcastFilter {
9 | private final Object broadcastFilter;
10 | private final ReceiverList receiverList;
11 |
12 |
13 | public BroadcastFilter(Object broadcastFilter) {
14 | this.broadcastFilter = broadcastFilter;
15 | this.receiverList = new ReceiverList(XposedHelpers.getObjectField(broadcastFilter, FieldEnum.receiverList));
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/BroadcastQueue.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import de.robv.android.xposed.XposedHelpers;
5 | import lombok.Data;
6 |
7 | @Data
8 | public class BroadcastQueue {
9 | private final Object broadcastQueue;
10 | private ActivityManagerService activityManagerService;
11 |
12 | public BroadcastQueue(Object broadcastQueue) {
13 | this.broadcastQueue = broadcastQueue;
14 | this.activityManagerService = new ActivityManagerService(XposedHelpers.getObjectField(broadcastQueue, FieldEnum.mService));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ComponentName.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import de.robv.android.xposed.XposedHelpers;
5 | import lombok.Data;
6 |
7 | @Data
8 | public class ComponentName {
9 | private final Object componentName;
10 | private final String packageName;
11 |
12 | public ComponentName(Object componentName) {
13 | this.componentName = componentName;
14 | this.packageName = (String) XposedHelpers.getObjectField(componentName, FieldEnum.mPackage);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/Event.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import de.robv.android.xposed.XposedHelpers;
4 |
5 | public class Event {
6 |
7 | public final static String Event = "android.app.usage.UsageEvents.Event";
8 | public final static String ACTIVITY_RESUMED = "ACTIVITY_RESUMED";
9 | public final static String ACTIVITY_PAUSED = "ACTIVITY_PAUSED";
10 | public final static String ACTIVITY_STOPPED = "ACTIVITY_STOPPED";
11 | public final static String ACTIVITY_DESTROYED = "ACTIVITY_DESTROYED";
12 |
13 | public static Class> getEvent(ClassLoader classLoader) {
14 | return XposedHelpers.findClass(Event, classLoader);
15 | }
16 |
17 | public static int ACTIVITY_RESUMED(ClassLoader classLoader) {
18 | Class> Event = getEvent(classLoader);
19 | return XposedHelpers.getStaticIntField(Event, ACTIVITY_RESUMED);
20 | }
21 |
22 | public static int ACTIVITY_PAUSED(ClassLoader classLoader) {
23 | Class> Event = getEvent(classLoader);
24 | return XposedHelpers.getStaticIntField(Event, ACTIVITY_PAUSED);
25 | }
26 | public static int ACTIVITY_STOPPED(ClassLoader classLoader) {
27 | Class> Event = getEvent(classLoader);
28 | return XposedHelpers.getStaticIntField(Event, ACTIVITY_STOPPED);
29 | }
30 |
31 | public static int ACTIVITY_DESTROYED(ClassLoader classLoader) {
32 | Class> Event = getEvent(classLoader);
33 | return XposedHelpers.getStaticIntField(Event, ACTIVITY_DESTROYED);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ProcessList.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import cn.myflv.android.noactive.entity.ClassEnum;
7 | import cn.myflv.android.noactive.entity.FieldEnum;
8 | import cn.myflv.android.noactive.entity.MethodEnum;
9 | import de.robv.android.xposed.XposedHelpers;
10 | import lombok.Data;
11 |
12 | @Data
13 | public class ProcessList {
14 | private final Object processList;
15 | private final List processRecords = new ArrayList<>();
16 |
17 | public ProcessList(Object processList) {
18 | this.processList = processList;
19 | try {
20 | List> processRecordList = (List>) XposedHelpers.getObjectField(processList, FieldEnum.mLruProcesses);
21 | for (Object proc : processRecordList) {
22 | ProcessRecord processRecord = new ProcessRecord(proc);
23 | processRecords.add(processRecord);
24 | }
25 | } catch (Exception ignored) {
26 |
27 | }
28 | }
29 |
30 | public static void setOomAdj(ClassLoader classLoader,int pid, int uid, int oomAdj) {
31 | Class> ProcessList = XposedHelpers.findClass(ClassEnum.ProcessList, classLoader);
32 | XposedHelpers.callStaticMethod(ProcessList, MethodEnum.setOomAdj,pid,uid,oomAdj);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ProcessRecord.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import android.annotation.TargetApi;
4 | import android.os.Build;
5 |
6 | import cn.myflv.android.noactive.entity.FieldEnum;
7 | import de.robv.android.xposed.XposedHelpers;
8 | import lombok.Data;
9 |
10 | @Data
11 | public class ProcessRecord {
12 | private final int uid;
13 | private final int pid;
14 | private final String processName;
15 | private final int userId;
16 | private final ApplicationInfo applicationInfo;
17 | private Object processRecord;
18 |
19 |
20 | public ProcessRecord(Object processRecord) {
21 | this.processRecord = processRecord;
22 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
23 | this.pid = XposedHelpers.getIntField(processRecord, FieldEnum.mPid);
24 | } else {
25 | this.pid = XposedHelpers.getIntField(processRecord, FieldEnum.pid);
26 | }
27 | this.uid = XposedHelpers.getIntField(processRecord, FieldEnum.uid);
28 | this.processName = (String) XposedHelpers.getObjectField(processRecord, FieldEnum.processName);
29 | this.userId = XposedHelpers.getIntField(processRecord, FieldEnum.userId);
30 | this.applicationInfo = new ApplicationInfo(XposedHelpers.getObjectField(processRecord, FieldEnum.info));
31 | }
32 |
33 | public void setCurAdj(int curAdj) {
34 | XposedHelpers.setIntField(processRecord, FieldEnum.curAdj, curAdj);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ProcessStateRecord.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import de.robv.android.xposed.XposedHelpers;
5 | import lombok.Data;
6 |
7 | @Data
8 | public class ProcessStateRecord {
9 | private final Object processStateRecord;
10 | private final ProcessRecord processRecord;
11 |
12 | public ProcessStateRecord(Object processStateRecord) {
13 | this.processStateRecord = processStateRecord;
14 | this.processRecord = new ProcessRecord(XposedHelpers.getObjectField(processStateRecord, FieldEnum.mApp));
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/server/ReceiverList.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.server;
2 |
3 | import cn.myflv.android.noactive.entity.FieldEnum;
4 | import cn.myflv.android.noactive.utils.Log;
5 | import de.robv.android.xposed.XC_MethodHook;
6 | import de.robv.android.xposed.XposedHelpers;
7 | import lombok.Data;
8 |
9 | @Data
10 | public class ReceiverList {
11 | private final Object receiverList;
12 | private ProcessRecord processRecord;
13 |
14 | public ReceiverList(Object receiverList) {
15 | this.receiverList = receiverList;
16 | try {
17 | this.processRecord = new ProcessRecord(XposedHelpers.getObjectField(receiverList, FieldEnum.app));
18 | } catch (Exception ignored) {
19 | }
20 | }
21 |
22 |
23 | public void clear() {
24 | XposedHelpers.setObjectField(receiverList, FieldEnum.app, null);
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/utils/ConfigFileObserver.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.utils;
2 |
3 | import android.os.FileObserver;
4 |
5 | import java.util.HashSet;
6 | import java.util.Set;
7 |
8 | import cn.myflv.android.noactive.entity.MemData;
9 |
10 | public class ConfigFileObserver extends FileObserver {
11 | private final MemData memData;
12 |
13 | public ConfigFileObserver(MemData memData) {
14 | super(FreezerConfig.ConfigDir);
15 | this.memData = memData;
16 | FreezerConfig.checkAndInit();
17 | reload();
18 | }
19 |
20 | @Override
21 | public void startWatching() {
22 | super.startWatching();
23 | for (String file : FreezerConfig.listenConfig) {
24 | Log.d("Start monitor " + file);
25 | }
26 | }
27 |
28 | @Override
29 | public void onEvent(int event, String path) {
30 | int e = event & ALL_EVENTS;
31 | switch (e) {
32 | case DELETE:
33 | case DELETE_SELF:
34 | FreezerConfig.checkAndInit();
35 | break;
36 | case MODIFY:
37 | case MOVE_SELF:
38 | ThreadUtil.sleep(2000);
39 | reload();
40 | }
41 | }
42 |
43 | public void reload() {
44 | for (String file : FreezerConfig.listenConfig) {
45 | Log.d("Reload " + file);
46 | Set newConfig = new HashSet<>(FreezerConfig.get(file));
47 | switch (file) {
48 | case FreezerConfig.whiteAppConfig:
49 | memData.setWhiteApps(newConfig);
50 | break;
51 | case FreezerConfig.whiteProcessConfig:
52 | memData.setWhiteProcessList(newConfig);
53 | break;
54 | case FreezerConfig.killProcessConfig:
55 | memData.setKillProcessList(newConfig);
56 | break;
57 | case FreezerConfig.blackSystemAppConfig:
58 | memData.setBlackSystemApps(newConfig);
59 | break;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/utils/FreezeUtils.java:
--------------------------------------------------------------------------------
1 | //
2 | // Decompiled by Jadx - 917ms
3 | //
4 | package cn.myflv.android.noactive.utils;
5 |
6 | import android.os.Process;
7 |
8 | import java.io.BufferedReader;
9 | import java.io.FileReader;
10 | import java.io.IOException;
11 | import java.io.PrintWriter;
12 | import java.util.ArrayList;
13 | import java.util.List;
14 |
15 | import cn.myflv.android.noactive.entity.ClassEnum;
16 | import cn.myflv.android.noactive.entity.MethodEnum;
17 | import cn.myflv.android.noactive.server.ProcessRecord;
18 | import de.robv.android.xposed.XposedHelpers;
19 |
20 | public class FreezeUtils {
21 |
22 | private final static int CONT = 18;
23 |
24 | private static final int FREEZE_ACTION = 1;
25 | private static final int UNFREEZE_ACTION = 0;
26 |
27 | private static final String V1_FREEZER_FROZEN_PORCS = "/sys/fs/cgroup/freezer/perf/frozen/cgroup.procs";
28 | private static final String V1_FREEZER_THAWED_PORCS = "/sys/fs/cgroup/freezer/perf/thawed/cgroup.procs";
29 |
30 | private final boolean freezerApi;
31 | private final int freezerVersion;
32 | private final int stopSignal;
33 | private final boolean useKill;
34 | private final ClassLoader classLoader;
35 |
36 |
37 | public FreezeUtils(ClassLoader classLoader) {
38 | this.classLoader = classLoader;
39 | String freezerVersion = FreezerConfig.getFreezerVersion(classLoader);
40 | switch (freezerVersion) {
41 | case FreezerConfig.API:
42 | this.freezerApi = true;
43 | this.freezerVersion = 2;
44 | break;
45 | case FreezerConfig.V2:
46 | this.freezerApi = false;
47 | this.freezerVersion = 2;
48 | break;
49 | case FreezerConfig.V1:
50 | default:
51 | this.freezerApi = false;
52 | this.freezerVersion = 1;
53 | }
54 | this.stopSignal = FreezerConfig.getKillSignal();
55 | this.useKill = FreezerConfig.isUseKill();
56 | if (useKill) {
57 | Log.i("Kill -" + stopSignal);
58 | } else {
59 | Log.i("Freezer " + freezerVersion);
60 | }
61 | }
62 |
63 |
64 | public static List getFrozenPids() {
65 | List pids = new ArrayList<>();
66 | try {
67 | BufferedReader reader = new BufferedReader(new FileReader(V1_FREEZER_FROZEN_PORCS));
68 | while (true) {
69 | String line = reader.readLine();
70 | if (line == null) {
71 | break;
72 | }
73 | try {
74 | pids.add(Integer.parseInt(line));
75 | } catch (NumberFormatException ignored) {
76 | }
77 | }
78 | reader.close();
79 | } catch (IOException ignored) {
80 | }
81 | return pids;
82 | }
83 |
84 | public void freezer(ProcessRecord processRecord) {
85 | if (useKill) {
86 | Process.sendSignal(processRecord.getPid(), stopSignal);
87 | } else {
88 | if (freezerVersion == 2) {
89 | if (freezerApi) {
90 | setProcessFrozen(processRecord.getPid(), processRecord.getUid(), true);
91 | } else {
92 | freezePid(processRecord.getPid(), processRecord.getUid());
93 | }
94 | } else {
95 | freezePid(processRecord.getPid());
96 | }
97 | }
98 | }
99 |
100 | public void unFreezer(ProcessRecord processRecord) {
101 | if (useKill) {
102 | Process.sendSignal(processRecord.getPid(), CONT);
103 | } else {
104 | if (freezerVersion == 2) {
105 | if (freezerApi) {
106 | setProcessFrozen(processRecord.getPid(), processRecord.getUid(), false);
107 | } else {
108 | thawPid(processRecord.getPid(), processRecord.getUid());
109 | }
110 | } else {
111 | thawPid(processRecord.getPid());
112 | }
113 | }
114 | }
115 |
116 | public static boolean isFrozonPid(int pid) {
117 | return getFrozenPids().contains(pid);
118 | }
119 |
120 |
121 | public static void freezePid(int pid) {
122 | writeNode(V1_FREEZER_FROZEN_PORCS, pid);
123 | }
124 |
125 |
126 | public static void thawPid(int pid) {
127 | writeNode(V1_FREEZER_THAWED_PORCS, pid);
128 | }
129 |
130 |
131 | private static void writeNode(String path, int val) {
132 | try {
133 | PrintWriter writer = new PrintWriter(path);
134 | writer.write(Integer.toString(val));
135 | writer.close();
136 | } catch (IOException e) {
137 | Log.e("Freezer V1 failed: " + e.getMessage());
138 | }
139 | }
140 |
141 |
142 | private static void setFreezeAction(int pid, int uid, boolean action) {
143 | String path = "/sys/fs/cgroup/uid_" + uid + "/pid_" + pid + "/cgroup.freeze";
144 | try {
145 | PrintWriter writer = new PrintWriter(path);
146 | if (action) {
147 | writer.write(Integer.toString(FREEZE_ACTION));
148 | } else {
149 | writer.write(Integer.toString(UNFREEZE_ACTION));
150 | }
151 | writer.close();
152 | } catch (IOException e) {
153 | Log.e("Freezer V2 failed: " + e.getMessage());
154 | }
155 | }
156 |
157 | public static void thawPid(int pid, int uid) {
158 | setFreezeAction(pid, uid, false);
159 | }
160 |
161 |
162 | public static void freezePid(int pid, int uid) {
163 | setFreezeAction(pid, uid, true);
164 | }
165 |
166 | public static void kill(int pid) {
167 | Process.killProcess(pid);
168 | }
169 |
170 | public void setProcessFrozen(int pid, int uid, boolean frozen) {
171 | Class> Process = XposedHelpers.findClass(ClassEnum.Process, classLoader);
172 | XposedHelpers.callStaticMethod(Process, MethodEnum.setProcessFrozen, pid, uid, frozen);
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/utils/FreezerConfig.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.utils;
2 |
3 | import android.os.Build;
4 |
5 | import java.io.BufferedReader;
6 | import java.io.File;
7 | import java.io.FileNotFoundException;
8 | import java.io.FileReader;
9 | import java.io.IOException;
10 | import java.util.HashSet;
11 | import java.util.Set;
12 |
13 | import cn.myflv.android.noactive.entity.ClassEnum;
14 | import cn.myflv.android.noactive.entity.MethodEnum;
15 | import de.robv.android.xposed.XposedHelpers;
16 |
17 | public class FreezerConfig {
18 |
19 |
20 | public final static String ConfigDir = "/data/system/NoActive";
21 | public final static String whiteAppConfig = "whiteApp.conf";
22 | public final static String blackSystemAppConfig = "blackSystemApp.conf";
23 | public final static String whiteProcessConfig = "whiteProcess.conf";
24 | public final static String killProcessConfig = "killProcess.conf";
25 | public final static String disableOOM = "disable.oom";
26 | public final static String kill19 = "kill.19";
27 | public final static String kill20 = "kill.20";
28 | public final static String freezerV1 = "freezer.v1";
29 | public final static String freezerV2 = "freezer.v2";
30 | public final static String freezerApi = "freezer.api";
31 | public final static String colorOs = "color.os";
32 | public final static String API = "Api";
33 | public final static String V2 = "V2";
34 | public final static String V1 = "V1";
35 |
36 |
37 | public final static String[] listenConfig = {whiteAppConfig, whiteProcessConfig,
38 | killProcessConfig, blackSystemAppConfig};
39 |
40 |
41 | public static boolean isConfigOn(String configName) {
42 | File config = new File(ConfigDir, configName);
43 | return config.exists();
44 | }
45 |
46 | public static int getKillSignal() {
47 | if (isConfigOn(kill19)) {
48 | return 19;
49 | }
50 | if (isConfigOn(kill20)) {
51 | return 20;
52 | }
53 | return 19;
54 | }
55 |
56 |
57 | public static String getFreezerVersion(ClassLoader classLoader) {
58 | if (isConfigOn(freezerV2)) {
59 | return V2;
60 | }
61 | if (isConfigOn(freezerV1)) {
62 | return V1;
63 | }
64 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
65 | if (isConfigOn(freezerApi)) {
66 | return API;
67 | }
68 | Class> CachedAppOptimizer = XposedHelpers.findClass(ClassEnum.CachedAppOptimizer, classLoader);
69 | boolean isSupportV2 = (boolean) XposedHelpers.callStaticMethod(CachedAppOptimizer, MethodEnum.isFreezerSupported);
70 | if (isSupportV2) {
71 | return V2;
72 | }
73 | }
74 | return V1;
75 | }
76 |
77 |
78 | public static boolean isUseKill() {
79 | return isConfigOn(kill19) || isConfigOn(kill20);
80 | }
81 |
82 | public static boolean isColorOs() {
83 | return isConfigOn(colorOs);
84 | }
85 |
86 |
87 | public static void checkAndInit() {
88 | File configDir = new File(ConfigDir);
89 | if (!configDir.exists()) {
90 | boolean mkdir = configDir.mkdir();
91 | if (!mkdir) return;
92 | Log.i("Init config dir");
93 | }
94 | for (String configName : listenConfig) {
95 | File config = new File(configDir, configName);
96 | if (!config.exists()) {
97 | createFile(config);
98 | Log.i("Init " + configName);
99 | }
100 | }
101 | }
102 |
103 |
104 | public static Set get(String name) {
105 | Set set = new HashSet<>();
106 | try {
107 | File file = new File(ConfigDir, name);
108 | BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
109 | String line;
110 | while ((line = bufferedReader.readLine()) != null) {
111 | if ("".equals(line.trim())) continue;
112 | if (line.startsWith("#")) continue;
113 | set.add(line.trim());
114 | Log.i(name.replace(".conf", "") + " add " + line);
115 | }
116 | bufferedReader.close();
117 | } catch (FileNotFoundException fileNotFoundException) {
118 | Log.e(name + " file not found");
119 | } catch (IOException ioException) {
120 | Log.e(name + " file read filed");
121 | }
122 | return set;
123 | }
124 |
125 | public static void createFile(File file) {
126 | try {
127 | boolean newFile = file.createNewFile();
128 | if (!newFile) {
129 | throw new IOException();
130 | }
131 | } catch (IOException e) {
132 | Log.e(file.getName() + " file create filed");
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/utils/Log.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.utils;
2 |
3 | import java.io.File;
4 |
5 | import de.robv.android.xposed.XposedBridge;
6 |
7 | public class Log {
8 | private final static String TAG = "NoActive";
9 | private static final boolean isDebug;
10 | private final static String ERROR = "error";
11 | private final static String WARN = "warn";
12 | private final static String INFO = "info";
13 | private final static String DEBUG = "debug";
14 |
15 | static {
16 | File config = new File(FreezerConfig.ConfigDir, "debug");
17 | isDebug = config.exists();
18 | i("Debug " + (isDebug ? "on" : "off"));
19 | }
20 |
21 | public static void d(String msg) {
22 | if (isDebug) {
23 | unify(DEBUG, msg);
24 | }
25 | }
26 |
27 | public static void i(String msg) {
28 | unify(INFO, msg);
29 | }
30 |
31 | public static void w(String msg) {
32 | unify(WARN, msg);
33 | }
34 |
35 | public static void e(String msg) {
36 | unify(ERROR, msg);
37 | }
38 |
39 |
40 | public static void e(String msg, Throwable throwable) {
41 | unify(ERROR, msg + ": " + throwable.getMessage());
42 | }
43 |
44 | public static void unify(String level, String msg) {
45 | XposedBridge.log(TAG + "(" + level + ") -> " + msg);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/myflv/android/noactive/utils/ThreadUtil.java:
--------------------------------------------------------------------------------
1 | package cn.myflv.android.noactive.utils;
2 |
3 | public class ThreadUtil {
4 | public static void sleep(int ms) {
5 | try {
6 | Thread.sleep(ms);
7 | } catch (InterruptedException ignored) {
8 | Log.w("Thread sleep failed");
9 | }
10 | }
11 |
12 | public static void sleep(long ms) {
13 | try {
14 | Thread.sleep(ms);
15 | } catch (InterruptedException ignored) {
16 | Log.w("Thread sleep failed");
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_logo.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_logo_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-hdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-hdpi/ic_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-hdpi/ic_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-mdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-mdpi/ic_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-mdpi/ic_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xhdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xhdpi/ic_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xhdpi/ic_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxhdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxhdpi/ic_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxhdpi/ic_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxxhdpi/ic_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_logo_foreground.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxxhdpi/ic_logo_foreground.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_logo_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/app/src/main/res/mipmap-xxxhdpi/ic_logo_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/array.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - android
5 | - com.miui.powerkeeper
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3DDC84
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | NoActive
3 | Let app no active in background
4 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | id 'com.android.application' version '7.1.3' apply false
4 | id 'com.android.library' version '7.1.3' apply false
5 | }
6 |
7 | task clean(type: Delete) {
8 | delete rootProject.buildDir
9 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Enables namespacing of each library's R class so that its R class includes only the
19 | # resources declared in the library itself and none from the library's dependencies,
20 | # thereby reducing the size of the R class for that library
21 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/myflavor/NoActive/ec57340fd2749d3e026e46a04bff6305f3977a78/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed May 18 23:46:06 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | maven { url "https://api.xposed.info/" }
14 | }
15 | }
16 | rootProject.name = "NoActive"
17 | include ':app'
18 |
--------------------------------------------------------------------------------