Android应用监控自身被卸载的几种姿势

原理就是在Linux系统中,父进程死亡,子进程会被init进程接管,并不会直接死亡,我们只需要fork一个子进程循环检测本应用文件夹是否存在或者使用Inotify机制检测应用卸载时发生的文件删除操作再或者监控日志等可以明显判断自身被卸载的特征即可

我们先来看如何使用Inotify机制来监控应用文件被删除的操作,先fork一个子进程,在子进程里初始化一个inotify_init实例,然后添加到监控队列,再阻塞,当阻塞的位置读取到数据,说明发生了删除操作,所以可以间接判断出应用被卸载,直接执行需要执行的操作,然后删除该文件的监控,最后子进程退出

#include <jni.h>
#include <string>
#include <cstdio>
#include <cstring>
#include <android/log.h>
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <sys/inotify.h>
using namespace std;

#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_INFO, "wnagzihxa1n", fmt, ##args)

extern "C"
JNIEXPORT void JNICALL
Java_com_wnagzihxa1n_demo_MainActivity_checkUninstall(
        JNIEnv *env,
        jobject /* this */) {

    LOGE("Start");

    pid_t pid = fork();
    if (pid < 0) {
        LOGE("Fork failed");
        exit(1);
    } else if (pid == 0) {
        LOGE("Enter child process");
        int fd = inotify_init();

        if (fd < 0) {
            LOGE("Call inotify_init() failed");
            exit(1);
        }
        LOGE("Call inotify_init() successfully");

        int monitor = inotify_add_watch(fd, "/data/data/com.wnagzihxa1n.demo", IN_DELETE);
        if (monitor < 0) {
            LOGE("Call inotify_add_watch() failed");
            exit(1);
        }
        LOGE("Call inotify_add_watch() successfully");

        void* pBuffer = malloc(sizeof(struct inotify_event));
        if (pBuffer == NULL) {
            LOGE("Malloc buffer failed");
            exit(1);
        }

        ssize_t readSize = read(fd, pBuffer, sizeof(struct inotify_event));

        free(pBuffer);
        inotify_rm_watch(fd, IN_DELETE);

        LOGE("**********Find Uninstall**********");
        exit(0);
    } else if (pid > 0) {
        return;
    } else {
        exit(1);
    }
}

不直接使用调试模式,而是先生成Release APK,然后安装再看整体的Log日志,当我们运行应用,可以看到应用启动了子进程,当我们卸载应用,发出了提示

12-05 10:08:55.112 2461-2461/? I/wnagzihxa1n: Start
12-05 10:08:55.122 2472-2472/? I/wnagzihxa1n: Enter child process
12-05 10:08:55.122 2472-2472/? I/wnagzihxa1n: Call inotify_init() successfully
12-05 10:08:55.122 2472-2472/? I/wnagzihxa1n: Call inotify_add_watch() successfully
12-05 10:09:15.352 2472-2472/? I/wnagzihxa1n: **********Find Uninstall**********

第二种是使用监控日志的方法,这个很简单了,我们来看卸载应用的时候日志会如何输出

12-05 10:50:47.599 1632-1676/? W/PackageManager: Couldn't remove dex file for package:  at location /data/app/org.strongswan.android-1/base.apk, retcode=-1
12-05 10:50:47.599 1632-1676/? I/PackageManager: reportApp :EventType Uninstall
12-05 10:50:47.599 1632-1676/? I/PackageManager: reportApp :Event  PackageName is org.strongswan.android
12-05 10:50:47.599 1632-1676/? I/PackageManager: reportApp :Event  returnCode is 1
12-05 10:50:47.600 2731-3185/? I/PackageManagerUtils: uninstall org.strongswan.android, result 1
12-05 10:50:47.713 2731-2731/? W/System.err: android.content.pm.PackageManager$NameNotFoundException: org.strongswan.android
12-05 10:50:47.736 2731-2731/? W/System.err:     at android.app.ApplicationPackageManager.getPackageInfo(ApplicationPackageManager.java:141)

所以只需要循环读取日志,出现uninstall $PACKAGENAME时就触发

还有很多其它卸载时可以看到的特征,无论是哪种,使用子进程是最为关键的核心