保活与杀活一直以来就是应用于系统间的博弈,魔高一尺道高一丈。各种奇奇怪怪的保活方案层中不穷,但是安卓随着系统版本的迭代,各种杀活策略更加严格,已经把流氓软件的可操作保活方法逼到了角落里面了··········
作为系统杀活阵营支持者的我由于收到应用大佬们十分过分的要求(需要系统拉活他们的应用或服务),所以不得不帮助他们进行系统保活·········
android系统原生方案
简单的系统保活方案就是防杀和拉活:
1.把要保活的应用加入powersave白名单,这样一般系统在待机模式低电量模式就不会去主动杀应用了。(但是不保证能在lowmemorykiller的屠刀中生存下来) 2020年了,Android后台保活还有戏吗?看我如何优雅的实现!
2.将想要保活的应用设置为特权应用,那么由于特权应用的优先级很高,lowpowerkiller跟影响不到它,并且特权应用系统可以自身拉活它。 Android应用的persistent属性
这一套组合拳打下来,系统绝不会去杀它的,即使意外挂掉,系统也能妥妥的将它拉活。 妥妥的保活
然而········· 应用大佬们并不买账,因为他们想要实现远程OTA应用,所以要允许系统被kill掉,然后再被拉活········ 并且应用大佬写的各种神奇功能的app被设置为特权应用成为持久男人之后,会和系统原生的应用有so兼容性冲突导致各种崩溃,应用不想放弃使用第三方的so,而且特权应用使用webview也有限制。所以导致应用大佬反对特权应用的实现方式········
那么问题就来了,系统自带的保活方案已经无法满足大佬们的要求,就要搞一个曲线救国的方法了。
民间大神方案
翻阅网上的帖子发现一个很有想法的保活方案,双进程native层保活方案,Android进程永生技术终极揭秘:进程被杀底层原理、APP应对被杀技巧
感觉为了保活这些大佬无所不用其极······ 原理不难理解,由于native调用binder的高效性,通过双进程间的文件锁互相感知,其中有一个被kill掉,另一个进程就要将拉活的intent进行发送出去,以对抗系统5ms一次的查杀。虽然原理简单但是实现很复杂啊········ 并且这个是要应用自己实现的,再并且 应用要允许自身OTA被kill掉进程树后被拉起。显然此方案是行不通的······
自己的方案
既然他人的方案行不通,那只有自己想办法实现了。好在系统代码在自己手中,可以在系统中做一些修改来实现应用保活。
起初构想: 实现一个app, 监听开机广播实现自启。然后监测apk被kill状态来拉起被kill的应用。 前提要保证自己开机启动并且自身保活,那么自身要成为特权应用。其次要得到应用被kill的信息,这就犯难了····· 我一个app咋能监测到其他应用被kill的消息?????
于是看了一些文章发现在进程挂掉后会有清理程序:
AMS当然会清理掉其对应的ProcessRecord,这就是cleanUpApplicationRecordLocked()的主要工作。然而,对于persistent应用,cleanUpApplicationRecordLocked()会尝试再次启动对应的应用进程。代码截选如下:
private final void cleanUpApplicationRecordLocked(ProcessRecord app,
boolean restarting,
boolean allowRestart, int index)
{
. . . . . .
. . . . . .
if (!app.persistent || app.isolated)
{
. . . . . .
mProcessNames.remove(app.processName, app.uid);
mIsolatedProcesses.remove(app.uid);
. . . . . .
}
else if (!app.removed)
{
if (mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
restart = true;
}
}
. . . . . .
. . . . . .
if (restart && !app.isolated)
{
mProcessNames.put(app.processName, app.uid, app);
startProcessLocked(app, "restart", app.processName);
}
else if (app.pid > 0 && app.pid != MY_PID)
{
. . . . . .
}
. . . . . .
}
在上面的函数中去将应用被kill的消息发送出去让自己的app去处理,显然发送广播比较简单,但是但觉不是很安全。由于自身应用还要常驻内存,干脆就把它写成系统服务。通过aidl来将kill后的回收event发送到自己实现的服务中,然后根据名单去拉活应用。自己去维护个白名单的话显然沟通成本比较高效率低下,干脆就让应用调用aidl接口自己设置。应用自己设置后白名单保存在xml文件中,应用只需设置一次,终身可以拉活。
应用去自己设置白名单,AMS将killevent发送给packagedaemon由其判断是否拉起。同时packagedaemon也向其他服务提供白名单查询接口,这样其他服务的一些权限就可在让应用自己设置了。此服务就是个中转处理,将设置的主导权交还给了应用。
代码写好了后就开始遇到SElinux这个拦路虎了,好在有现成的方法可以参考:
Android : 为系统服务添加 SELinux 权限 (Android 9.0)
Android8.1 新增系统自定义服务一 (SELinux权限)
Android 9.0 (P版本) SystemServer中的服务配置se linux权限
android 8.1 安全机制 — SEAndroid & SELinux
其他参考:
android java进程管理(六)之apk进程的回收