Thanox icon indicating copy to clipboard operation
Thanox copied to clipboard

Try to suspend the process--墓碑模式探索

Open countrysideboy opened this issue 4 years ago • 77 comments

如题,觉得目前的乖巧模式的不太压得住流氓们,app切换到后台后仍然占用cpu,于是探索使用冻结进程的方式,看作者大神后续能否增强(需要那种前台能用,后台压得死死的,切换回前台又恢复之前的会话)

在全局变量中添加 pause,把要压制的app添加进去;或者在目标应用详情,将电池设置为受限(为了检验效果,脚本与乖巧模式互斥,会自动关闭乖巧)

20220729更新: 1.修复未添加全局变量pause时脚本出错的bug; 2.添加参数自定义调整功能,其中DELAY = 45000,对应实现应用切回后台进入墓碑的等待时长(毫秒);修改为SU_EXE = 1,则使用kill -19命令来压制进程,需打开su插件支持(默认SU_EXE = 0,使用系统api进行压制,无需su插件)。 情景模式脚本

[
  {
        "name": "ForceIdle: auto pause or continue App",
        "description": "通过全局变量[pause]添加应用列表,或设置电池用量为受限,对回到后台的应用强制压制(墓碑)。\nForce pause the app while it switchs to backgroud, you need to pick app list in the global val name [pause].",
        "priority": 1,
        "condition": "frontPkgChanged == true",
        "actions": [
            "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
            "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
            "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
            "if(context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to) == android.app.AppOpsManager.MODE_IGNORED || (thanos.getProfileManager().isGlobalRuleVarByNameExists(\"pause\") && globalVarOf$pause.contains(to))){foreach(proc:context.getSystemService(context.ACTIVITY_SERVICE).getRunningAppProcesses()){if(proc.processName.contains(to)){android.os.Process.sendSignal(proc.pid,18);}}; context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT); context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(to,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE); if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)}; if(thanos.getActivityManager().isPkgBgRestricted(to)){thanos.getActivityManager().setPkgBgRestrictEnabled(to,false)}};",
            "DELAY = 45000; if(context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from) == android.app.AppOpsManager.MODE_IGNORED || (thanos.getProfileManager().isGlobalRuleVarByNameExists(\"pause\") && globalVarOf$pause.contains(from))){context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED); thanos.getActivityManager().setBlockAllService(from,true); thanos.getActivityManager().setBlockAllReceiver(from,true); thanos.getActivityManager().setBlockAllProvider(from,true); actor.delayed(DELAY,\"if(activity.getFrontAppPackage()!=from){if(thanos.getActivityManager().isPackageIdle(from)==false){context.getSystemService(context.ACTIVITY_SERVICE).getService().makePackageIdle(from,android.os.UserHandle.USER_ALL)}; if(activity.isInactive(from)==false){activity.setInactive(from)}; context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(from,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED); SU_EXE = 0; foreach(proc:context.getSystemService(context.ACTIVITY_SERVICE).getRunningAppProcesses()){if(proc.processName.contains(from)){if(SU_EXE == 1){su.exe(\\\"kill -19 \\\" + proc.pid);};if(SU_EXE != 1){android.os.Process.sendSignal(proc.pid,20);};}}}\")};"
         ]
     }
]

countrysideboy avatar Apr 05 '22 03:04 countrysideboy

目前已知问题: 1.好像压不住关联唤醒,压不住广播 2.使用冻结进程的方式,原脚本是用kill -19的方式,现在换成cgroup feeeze的方式,但从日志看,进程似乎仍会anr。

也许要写Xposed模块hook相关操作才能搞定上面的问题,但要处理后台自启,广播,wakelock,alarm,想想就麻烦,期待作者出暴力压制模式。目前thanox的权限管理够好用了,要是能压制后台,原生系统就更省电了。

countrysideboy avatar Apr 05 '22 03:04 countrysideboy

都是人才啊,我会抽时间学习研究一下。

Tornaco avatar Apr 05 '22 10:04 Tornaco

我编辑了一下这个issue,调整了一下代码格式化。

Tornaco avatar Apr 05 '22 12:04 Tornaco

最近我恰好在探索相关内容,但无奈自己没有安卓开发的能力,目前仅能做到脚本级别的冰冻后台(效率极度低下,基本就是暴力轮询)。Thanox在安卓平台层面应该能更好地处理相关功能实现。

@countrysideboy 你应该用到了cgroupv2的freezer功能,这个功能要求内核版本大于5.4、或者OEM将相关功能移植回低版本内核。而如果你的内核本来就支持了cgroupv2的freezer,那么你可以前往开发者选项 -> 对缓存应用暂停执行(Suspend execution for cached apps)

相关功能分析: https://juejin.cn/post/7078957393446453285

kill -SIGCONT会导致父进程异常,相关问题介绍: https://www.kernel.org/doc/Documentation/cgroup-v1/freezer-subsystem.txt 但不知道先用freezer冻结之后再用SIGSTOP冻结,并且在唤醒时先用SIGCONT解除掉SIGSTOP、再把freezer的冰冻状态解除,这样能不能规避父进程崩溃的问题。

@Tornaco cgroupv2 freezer有较高的内核版本要求,请考虑一下如果cgroupv2 freezer不可用的时候fallback回cgroupv1 freezer(支持这个的设备应该更加广泛)

freezer冻结和解冻请以整个应用为单位来进行,不然可能发生异常,比如应用的其它进程的异常捕捉认为应用未响应了于是尝试重启应用(脚本里的逐个pid冰冻是有问题的)。更好的做法是创建一个新的cgroup,并以应用包(或uid)为单位划分,冻结时将整个节点冻结而不是单独一个一个pid来。

最后,如果有其它通讯平台的联系方式可能会更方便些。

tzfljuv avatar Apr 10 '22 05:04 tzfljuv

看来研究得很深入啊。一开始我是直接冻结整个uid 而非pid,但是发现应用异常退出后,无论是否解冻,app都无法再打开,很奇怪,才转而冻结pid

countrysideboy avatar Apr 10 '22 05:04 countrysideboy

至于kill -19的方式,可能比较通用,但会anr,我试过去hook相关函数但没有成功。

另外就算能冻结成功,唤醒锁,闹钟,广播需要解决,不然还是会被关联唤醒。

countrysideboy avatar Apr 10 '22 06:04 countrysideboy

通过Hook安卓系统屏蔽ANR Screenshot_2022-05-03-08-02-14-587_me simpleHook

myflavor avatar May 03 '22 00:05 myflavor

[
    {
        "name": "冻结进程",
        "description": "当应用进入后台时冻结进程",
        "priority": 1,
        "condition": "frontPkgChanged == true && thanos.getActivityManager().isPkgSmartStandByEnabled(from)==true && !globalVarOf$whiteApps.contains(from)",
        "actions": [
            "su.exe(\"appops set \" +from + \" RUN_ANY_IN_BACKGROUND ignore\");",
            "su.exe(\"appops set \" +from + \" RUN_IN_BACKGROUND ignore\");",
            "su.exe(\"appops set \" +from + \" WAKE_LOCK ignore\");",
            "activity.setInactive(from);",
            "su.exe(\"am make-uid-idle \" +from);",
            "su.exe(\"am set-standby-bucket \" +from +\" restricted\");", "actor.delayed(2000,\"if(activity.getFrontAppPackage()!=from){su.exe(\\\"kill -19 `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
        ]
    }
]



[
    {
        "name": "解冻进程",
        "description": "当应用进入前台时解冻进程",
        "priority": 1,
        "condition": "frontPkgChanged == true && thanos.getActivityManager().isPkgSmartStandByEnabled(to)==true && !globalVarOf$whiteApps.contains(to)",
        "actions": [
            "su.exe(\"appops set \" +to + \" RUN_ANY_IN_BACKGROUND default\");",
            "su.exe(\"appops set \" +to + \" RUN_IN_BACKGROUND default\");",
            "su.exe(\"appops set \" +to + \" WAKE_LOCK default\");",
"su.exe(\"kill -18 `pgrep -f \"+ to + \"`\");",
            "su.exe(\"am set-standby-bucket \" +to +\" rare\");"
        ]
    }
]

myflavor avatar May 03 '22 04:05 myflavor

以下为使用kill方式冻结的脚本,做了一些禁用广播接收器方面的尝试

情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(to)",
    "actions": [
    "su.exe(\"kill -CONT `pgrep -f \"+ to + \"`\");",
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)};",
    "su.exe(\"am set-standby-bucket \" +to +\" rare\");"
    ]
  }
]

情景模式脚本2

[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(from) && activity.getFrontAppPackage()!=\"android\" ",
    "actions": [ 
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "if(thanos.getActivityManager().isBlockAllService(from)==false){thanos.getActivityManager().setBlockAllService(from,true)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(from)==false){thanos.getActivityManager().setBlockAllReceiver(from,true)};",
    "if(thanos.getActivityManager().isBlockAllProvider(from)==false){thanos.getActivityManager().setBlockAllProvider(from,true)};",
    "su.exe(\"am send-trim-memory \" +from+ \" TRIM_MEMORY_RUNNING_CRITICAL\");",
    "if(activity.isInactive(from)==false){activity.setInactive(from)};",
    "if(thanos.getActivityManager().isPackageIdle(from)==false){thanos.getActivityManager().idlePackage(from)};",
    "actor.delayed(15000,\"if(activity.getFrontAppPackage()!=from){su.exe(\\\"am make-uid-idle \\\" +from);su.exe(\\\"am set-standby-bucket \\\" +from +\\\" restricted\\\");su.exe(\\\"kill -STOP `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
    ] 
  }
]

已更新,希望最终能不用su.exe,调用系统api实现,比如sendsignal等

countrysideboy avatar May 24 '22 00:05 countrysideboy

OP_WAKE_LOCK应该是没用的 OP_RUN_ANY_IN_BACKGROUND比OP_RUN_IN_BACKGROUND限制多 下面的广播服务提供者阻止,不知道没有NoANR能不能正常了

myflavor avatar May 24 '22 04:05 myflavor

OP_WAKE_LOCK应该是没用的 OP_RUN_ANY_IN_BACKGROUND比OP_RUN_IN_BACKGROUND限制多 下面的广播服务提供者阻止,不知道没有NoANR能不能正常了

初步测试了一下,不使用NoANR模块好像可以,thanox作者似乎有处理这个问题。

countrysideboy avatar May 24 '22 04:05 countrysideboy

有看过/data/anr的日志吗

myflavor avatar May 24 '22 04:05 myflavor

也不知道Thanox怎么阻止的广播,我的NoANR是在分到APP之前去掉了广播,但是已经在执行的广播没去干扰,看这个情景模式也是延迟了15s,前台广播超时好像是10s,这样应该不存在后台后还有广播在执行了

myflavor avatar May 24 '22 04:05 myflavor

有看过/data/anr的日志吗

昨天我把文件清空了,要测久一点才能确认了

countrysideboy avatar May 24 '22 04:05 countrysideboy

冻结APP之后,直接锁屏,触发息屏广播,过个几秒,看看/data/anr有没有日志,如果有就是广播阻止的不彻底,如果没有,广播就成功阻止了,然后还有服务的ANR,就是挂后台,挂久一点,如果没有出现,那就没啥问题了,提供者的ANR我没看过了,NoANR去掉广播之后,屏蔽NoANR就没有任何问题了,所以我就没继续研究了

myflavor avatar May 24 '22 05:05 myflavor

用Thanox的阻止广播还有服务和提供者,会导致比如美团支付使用支付宝,而支付宝以及被阻止了,就会显示找不到支付宝

myflavor avatar May 24 '22 05:05 myflavor

用Thanox的阻止广播还有服务和提供者,会导致比如美团支付使用支付宝,而支付宝以及被阻止了,就会显示找不到支付宝

是吧,我试了下闲鱼跳转淘宝授权登录,好像也不行

countrysideboy avatar May 24 '22 05:05 countrysideboy

冻结APP之后,直接锁屏,触发息屏广播,过个几秒,看看/data/anr有没有日志,如果有就是广播阻止的不彻底,如果没有,广播就成功阻止了,然后还有服务的ANR,就是挂后台,挂久一点,如果没有出现,那就没啥问题了,提供者的ANR我没看过了,NoANR去掉广播之后,屏蔽NoANR就没有任何问题了,所以我就没继续研究了

测试了几下,暂时没发现有文件。

countrysideboy avatar May 24 '22 05:05 countrysideboy

还有就是,感觉15s延迟是不是太久了,进入后台那一会是最活跃的,如果改成3s还能不能正常,会不会就触发ANR了

myflavor avatar May 24 '22 05:05 myflavor

还有就是,感觉15s延迟是不是太久了,进入后台那一会是最活跃的,如果改成3s还能不能正常,会不会就触发ANR了

我试试。等待时间久些,是希望程序能进缓存。之前测试,短时间内发送make-uid-idle没有效果,要有那么一段时间后执行才有效

countrysideboy avatar May 24 '22 05:05 countrysideboy

以下为使用kill方式冻结的脚本,做了一些禁用广播接收器方面的尝试 情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(to)",
    "actions": [
    "su.exe(\"kill -CONT `pgrep -f \"+ to + \"`\");",
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)};",
    "su.exe(\"am set-standby-bucket \" +to +\" rare\");"
    ]
  }
]

情景模式脚本2

[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(from) && activity.getFrontAppPackage()!=\"android\" ",
    "actions": [ 
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "if(thanos.getActivityManager().isBlockAllService(from)==false){thanos.getActivityManager().setBlockAllService(from,true)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(from)==false){thanos.getActivityManager().setBlockAllReceiver(from,true)};",
    "if(thanos.getActivityManager().isBlockAllProvider(from)==false){thanos.getActivityManager().setBlockAllProvider(from,true)};",
    "su.exe(\"am send-trim-memory \" +from+ \" TRIM_MEMORY_RUNNING_CRITICAL\");",
    "if(activity.isInactive(from)==false){activity.setInactive(from)};",
    "if(thanos.getActivityManager().isPackageIdle(from)==false){thanos.getActivityManager().idlePackage(from)};",
    "actor.delayed(15000,\"if(activity.getFrontAppPackage()!=from){su.exe(\\\"am make-uid-idle \\\" +from);su.exe(\\\"am set-standby-bucket \\\" +from +\\\" restricted\\\");su.exe(\\\"kill -STOP `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
    ] 
  }
]

已更新,希望最终能不用su.exe,调用系统api实现,比如sendsignal等

发现如果直接把全局变量的软件取消后会出现奇怪的问题 比如qq和微信是连不上网 接不到新消息,停止应用和重新开机都不行恢复,只有停止冻结的那个情景模式,而且解冻的那个情景模式得开启,再打开一次软件才恢复正常,如果有人看到这个问题,请使用这个方法恢复app。

hanxin1997 avatar May 24 '22 06:05 hanxin1997

这个问题就是阻止广播还有服务以及提供者导致的,你可以关闭冻结脚本,开一遍被冻结过的脚本,然后移除冻结名单

myflavor avatar May 24 '22 06:05 myflavor

写了一版比较特殊的版本,不需要su.exe插件,不需要添加全局变量了(以前的脚本需要添加pause变量且加入app列表)。 开关应用是否墓碑的方式为:使用系统自带的电池设置,将目标app的“管理电池用量”设置为“受限”即可。 缺点是无法批量添加app,提供一种思路,权当系统鸡肋电量限制模式的增强版。 备注:需要安卓10以上。

情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true",
    "actions": [
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "if(context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to) == android.app.AppOpsManager.MODE_IGNORED){foreach(proc:context.getSystemService(context.ACTIVITY_SERVICE).getRunningAppProcesses()){if(proc.processName.contains(to)){android.os.Process.sendSignal(proc.pid,18);}}; context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT); context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(to,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE)}; if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)}; if(thanos.getActivityManager().isPkgBgRestricted(to)){thanos.getActivityManager().setPkgBgRestrictEnabled(to,false)};"
    ]
  }
]

情景模式脚本2


[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from) == android.app.AppOpsManager.MODE_IGNORED",
    "actions": [
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "thanos.getActivityManager().setBlockAllService(from,true);",
    "thanos.getActivityManager().setBlockAllReceiver(from,true);",
    "thanos.getActivityManager().setBlockAllProvider(from,true);",
    "actor.delayed(15000,\"if(activity.getFrontAppPackage()!=from){if(thanos.getActivityManager().isPackageIdle(from)==false && android.os.Build.VERSION.SDK_INT < 32){context.getSystemService(context.ACTIVITY_SERVICE).getService().makePackageIdle(from,android.os.UserHandle.USER_ALL)};if(activity.isInactive(from)==false && android.os.Build.VERSION.SDK_INT < 32){activity.setInactive(from)};context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(from,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED);foreach(proc:context.getSystemService(context.ACTIVITY_SERVICE).getRunningAppProcesses()){if(proc.processName.contains(from)){android.os.Process.sendSignal(proc.pid,20);}}}\");"
    ] 
  }
]

countrysideboy avatar May 24 '22 14:05 countrysideboy

以下为使用kill方式冻结的脚本,做了一些禁用广播接收器方面的尝试 情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(to)",
    "actions": [
    "su.exe(\"kill -CONT `pgrep -f \"+ to + \"`\");",
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)};",
    "su.exe(\"am set-standby-bucket \" +to +\" rare\");"
    ]
  }
]

情景模式脚本2

[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(from) && activity.getFrontAppPackage()!=\"android\" ",
    "actions": [ 
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "if(thanos.getActivityManager().isBlockAllService(from)==false){thanos.getActivityManager().setBlockAllService(from,true)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(from)==false){thanos.getActivityManager().setBlockAllReceiver(from,true)};",
    "if(thanos.getActivityManager().isBlockAllProvider(from)==false){thanos.getActivityManager().setBlockAllProvider(from,true)};",
    "su.exe(\"am send-trim-memory \" +from+ \" TRIM_MEMORY_RUNNING_CRITICAL\");",
    "if(activity.isInactive(from)==false){activity.setInactive(from)};",
    "if(thanos.getActivityManager().isPackageIdle(from)==false){thanos.getActivityManager().idlePackage(from)};",
    "actor.delayed(15000,\"if(activity.getFrontAppPackage()!=from){su.exe(\\\"am make-uid-idle \\\" +from);su.exe(\\\"am set-standby-bucket \\\" +from +\\\" restricted\\\");su.exe(\\\"kill -STOP `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
    ] 
  }
]

已更新,希望最终能不用su.exe,调用系统api实现,比如sendsignal等

发现如果直接把全局变量的软件取消后会出现奇怪的问题 比如qq和微信是连不上网 接不到新消息,停止应用和重新开机都不行恢复,只有停止冻结的那个情景模式,而且解冻的那个情景模式得开启,再打开一次软件才恢复正常,如果有人看到这个问题,请使用这个方法恢复app。

这个阻止广播还有服务以及提供者还会因为开关vpn之后网络异常, 副作用挺大的 , 比如软件开着vpn 回到桌面, 关闭vpn,15s后再打开软件,软件依旧无法上网,需要重新打开vpn之后才能正常连接网络

Teddy-Zhu avatar May 26 '22 08:05 Teddy-Zhu

以下为使用kill方式冻结的脚本,做了一些禁用广播接收器方面的尝试 情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(to)",
    "actions": [
    "su.exe(\"kill -CONT `pgrep -f \"+ to + \"`\");",
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT);",
    "if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)};",
    "su.exe(\"am set-standby-bucket \" +to +\" rare\");"
    ]
  }
]

情景模式脚本2

[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && globalVarOf$pause.contains(from) && activity.getFrontAppPackage()!=\"android\" ",
    "actions": [ 
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_RUN_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "if(thanos.getActivityManager().isBlockAllService(from)==false){thanos.getActivityManager().setBlockAllService(from,true)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(from)==false){thanos.getActivityManager().setBlockAllReceiver(from,true)};",
    "if(thanos.getActivityManager().isBlockAllProvider(from)==false){thanos.getActivityManager().setBlockAllProvider(from,true)};",
    "su.exe(\"am send-trim-memory \" +from+ \" TRIM_MEMORY_RUNNING_CRITICAL\");",
    "if(activity.isInactive(from)==false){activity.setInactive(from)};",
    "if(thanos.getActivityManager().isPackageIdle(from)==false){thanos.getActivityManager().idlePackage(from)};",
    "actor.delayed(15000,\"if(activity.getFrontAppPackage()!=from){su.exe(\\\"am make-uid-idle \\\" +from);su.exe(\\\"am set-standby-bucket \\\" +from +\\\" restricted\\\");su.exe(\\\"kill -STOP `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
    ] 
  }
]

已更新,希望最终能不用su.exe,调用系统api实现,比如sendsignal等

发现如果直接把全局变量的软件取消后会出现奇怪的问题 比如qq和微信是连不上网 接不到新消息,停止应用和重新开机都不行恢复,只有停止冻结的那个情景模式,而且解冻的那个情景模式得开启,再打开一次软件才恢复正常,如果有人看到这个问题,请使用这个方法恢复app。

这个阻止广播还有服务以及提供者还会因为开关vpn之后网络异常, 副作用挺大的 , 比如软件开着vpn 回到桌面, 关闭vpn,15s后再打开软件,软件依旧无法上网,需要重新打开vpn之后才能正常连接网络

如果是这样,或许可以触发一个网络连接变化的广播

countrysideboy avatar May 27 '22 13:05 countrysideboy

现在thanox能实现识别显示在应用上层的服务和前台服务以防止这些应用被墓碑吗

Moderpach avatar Jun 01 '22 10:06 Moderpach

现在thanox能实现识别显示在应用上层的服务和前台服务以防止这些应用被墓碑吗

不清楚,暂时没找到方法

countrysideboy avatar Jun 02 '22 04:06 countrysideboy

写了一版比较特殊的版本,不需要添加全局变量了(以前的脚本需要添加pause变量且加入app列表)。 开关应用是否墓碑的方式为:使用系统自带的电池设置,将目标app的“管理电池用量”设置为“受限”即可。 缺点是无法批量添加app,提供一种思路,权当系统鸡肋电量限制模式的增强版。 备注:需要安卓10以上。

情景模式脚本1

[
  {
    "name": "ForceIdle: continue App",
    "description": "切回前台,取消压制",
    "priority": 1,
    "condition": "frontPkgChanged == true ",
    "actions": [
    "if(thanos.getActivityManager().isBlockAllService(to)){thanos.getActivityManager().setBlockAllService(to,false)};",
    "if(thanos.getActivityManager().isBlockAllReceiver(to)){thanos.getActivityManager().setBlockAllReceiver(to,false)};",
    "if(thanos.getActivityManager().isBlockAllProvider(to)){thanos.getActivityManager().setBlockAllProvider(to,false)};",
    "if(context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(to), to) == android.app.AppOpsManager.MODE_IGNORED){su.exe(\"kill -CONT `pgrep -f \"+ to + \"`\"); context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(to), to, android.app.AppOpsManager.MODE_DEFAULT); if(thanos.getActivityManager().isPkgSmartStandByEnabled(to)){thanos.getActivityManager().setPkgSmartStandByEnabled(to,false)}; context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(to,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE)};"
    ]
  }
]

情景模式脚本2

[
  {
    "name": "ForceIdle: pause App",
    "description": "切到后台,自动压制",
    "priority": 2,
    "condition": "frontPkgChanged == true && context.getSystemService(context.APP_OPS_SERVICE).unsafeCheckOp(android.app.AppOpsManager.OPSTR_RUN_ANY_IN_BACKGROUND, thanos.getPkgManager().getUidForPkgName(from), from) == android.app.AppOpsManager.MODE_IGNORED ",
    "actions": [ 
    "context.getSystemService(context.APP_OPS_SERVICE).setMode(android.app.AppOpsManager.OP_WAKE_LOCK, thanos.getPkgManager().getUidForPkgName(from), from, android.app.AppOpsManager.MODE_IGNORED);",
    "thanos.getActivityManager().setBlockAllService(from,true);",
    "thanos.getActivityManager().setBlockAllReceiver(from,true);",
    "thanos.getActivityManager().setBlockAllProvider(from,true);",
    "if(activity.isInactive(from)==false){activity.setInactive(from)};",
    "if(thanos.getActivityManager().isPackageIdle(from)==false){context.getSystemService(context.ACTIVITY_SERVICE).getService().makePackageIdle(from,android.os.UserHandle.USER_ALL)};",
    "actor.delayed(1500,\"if(activity.getFrontAppPackage()!=from){context.getSystemService(context.ACTIVITY_SERVICE).getService().makePackageIdle(from,android.os.UserHandle.USER_ALL); context.getSystemService(context.USAGE_STATS_SERVICE).setAppStandbyBucket(from,android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED);su.exe(\\\"kill -STOP `pgrep -f \\\"+ from+ \\\"`\\\")}\");"
    ] 
  }
]

20220602:这是我目前自己在用的版本,会自动关闭乖巧,通过设置某个app的电池限制模式来决定是否墓碑

countrysideboy avatar Jun 02 '22 04:06 countrysideboy

现在thanox能实现识别显示在应用上层的服务和前台服务以防止这些应用被墓碑吗

3.9.9 新增 API可以识别应用是否有可见window,可用于判断其是否有悬浮窗展示。

android/android_framework/base/src/main/java/github/tornaco/android/thanos/core/wm/WindowManager.java

@SneakyThrows
    public List<WindowState> getVisibleWindows() {
        return server.getVisibleWindows();
    }

    @SneakyThrows
    public boolean hasVisibleWindows(String pkgName) {
        return getVisibleWindows().stream().anyMatch(windowState -> windowState.packageName.equals(pkgName));
    }

    @SneakyThrows
    public boolean hasVisibleWindows(Pkg pkg) {
        return getVisibleWindows().stream().anyMatch(windowState -> windowState.packageName.equals(pkg.getPkgName())
                && UserHandle.getUserId(windowState.uid) == pkg.getUserId());
    }

示例:

action: "if (thanos.windowManager.hasVisibleWindows(xxxxx.xxx.xxx)) ..."

Tornaco avatar Jun 03 '22 14:06 Tornaco

这样确实可以避免app被冻结了,不过小窗程序在进入后台,不会触发任何条件,这个不知道有没有什么好的办法了

myflavor avatar Jun 03 '22 14:06 myflavor