监控应用自身被卸载

目录

今天思考的一个问题是如何让应用监控到自己被卸载,然后在卸载的时候执行一些操作,比如向服务器发送一个报告,或者引导用户到一个卸载反馈页上去,比如360手机助手那样。然后就在网上上看到了这篇文章android应用监控被卸载还有它的续集,github上有人根据他的方案做出了一个demo:https://github.com/sevenler/Uninstall_Statics。这个demo我测试过,是可用的。只要不是用户通过进程管理器把整个进程都kill掉,就能够在应用自身被卸载的时候调用c写的native代码,感觉上是利用了一个时间差吧。

blog上介绍了其原理,通过java进程调用native代码,然后fork出一个子进程,在子进程中利用linux内核的inotify机制来监听文件夹的状态,如果一旦被删除就认定为应用被卸载了,就触发后续的代码。这里的inotify机制的利用很有意思,避免了轮询带来对系统资源的消耗和浪费。下面是摘出来的代码:

//fork子进程,以执行轮询任务
pid_t pid = fork();
 if (pid < 0)
 {
     //出错log
     LOG_ERROR((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
             , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "fork failed !!!"), &b_IS_COPY));
 }
 else if (pid == 0)
 {
     //子进程注册"/data/data/pym.test.uninstalledobserver"目录监听器
     int fileDescriptor = inotify_init();
     if (fileDescriptor < 0)
     {
         LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_init failed !!!"), &b_IS_COPY));
 
          exit(1);
     }
 
     int watchDescriptor;
     watchDescriptor = inotify_add_watch(fileDescriptor, "/data/data/pym.test.uninstalledobserver", IN_DELETE);
     if (watchDescriptor < 0)
     {
         LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "inotify_add_watch failed !!!"), &b_IS_COPY));
 
         exit(1);
     }
 
     //分配缓存,以便读取event,缓存大小=一个struct inotify_event的大小,这样一次处理一个event
     void *p_buf = malloc(sizeof(struct inotify_event));
     if (p_buf == NULL)
     {
         LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "malloc failed !!!"), &b_IS_COPY));
 
         exit(1);
     }
     //开始监听
     LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "start observer"), &b_IS_COPY));
     size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
 
     //read会阻塞进程,走到这里说明收到目录被删除的事件,注销监听器
     free(p_buf);
     inotify_rm_watch(fileDescriptor, IN_DELETE);
 
     //目录不存在log
     LOG_DEBUG((*env)->GetStringUTFChars(env, tag, &b_IS_COPY)
                 , (*env)->GetStringUTFChars(env, (*env)->NewStringUTF(env, "uninstalled"), &b_IS_COPY));
 
     //执行命令am start -a android.intent.action.VIEW -d http://shouji.360.cn/web/uninstall/uninstall.html
     execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", "http://shouji.360.cn/web/uninstall/uninstall.html", (char *)NULL);

在手机上查看进程,可以看到一个主进程,一个fork出来的进程,如下图: fork process
结束主进程之后,子进程可以依然存在,并完成预定的任务。