Android 監聽應用解除安裝:彈出反饋介面並上傳客戶端資料

idaretobe發表於2015-01-14

/* 標頭檔案begin */
#include "UninstalledObserver.h"
/* 標頭檔案end */

#ifdef __cplusplus
extern "C"
{
#endif

/* 內全域性變數begin */
static char TAG[] = "UninstalledObserver";
static jboolean isCopy = JNI_TRUE;
static const char ObserverProcessName[] = "com.baidu.video:observer";
static const char APP_DIR[] = "/data/data/com.baidu.video/lib";
static const char APP_FILES_DIR[] = "/data/data/com.baidu.video/files";
static  char* APP_OBSERVED_FILE = "/data/data/com.baidu.video/files/observedFile";
//static  char* APP_OBSERVED_FILE = "/data/data/com.baidu.video/databases/bdplayer_database";
static  char* APP_FEEDBACK_URL = "http://www.baidu.com";
static const char APP_LOCK_FILE[] = "/data/data/com.baidu.video/files/lockFile";
int  watchDescriptor;
int  fileDescriptor;
pid_t observer=-1;
/* 內全域性變數 */

/*
 * Class:     com_baidu_video_UninstalledObserver
 * Method:    init
 * Signature: ()V
 * return: 子程式pid
 */
JNIEXPORT int JNICALL Java_com_baidu_video_UninstalledObserver_init(JNIEnv *env, jobject obj, jstring userSerial,
        jstring path,jstring url,jstring uploadData)
{
        int isObserverAlive = -1;
        isObserverAlive = isObserverProcessAlive(APP_OBSERVED_FILE);
        if(isObserverAlive == 0){
            //判斷到監聽程式還在
            return observer;
        }
        if(path != NULL){
            APP_OBSERVED_FILE=(*env)->GetStringUTFChars(env,path, 0);
        }
        // fork子程式,以執行輪詢任務
        pid_t pid = fork();
        if (pid < 0)
        {
            //fork failed !!!
            exit(1);
        }
        else if (pid == 0)
        {
            //儲存監聽程式id
            writePidFile(APP_OBSERVED_FILE);
            // 分配空間,以便讀取event
            void *p_buf = malloc(sizeof(struct inotify_event));
            if (p_buf == NULL)
            {
                //malloc failed !!!
                exit(1);
            }
            // 分配空間,以便列印mask
            int maskStrLength = 7 + 10 + 1;// mask=0x佔7位元組,32位整形數最大為10位,轉換為字串佔10位元組,'\0'佔1位元組
            char *p_maskStr = malloc(maskStrLength);
            if (p_maskStr == NULL)
            {
                free(p_buf);
                exit(1);
            }
            // 開始監聽
            startObserver(env,p_buf,p_maskStr);
            while(1)
            {
                // 開始迴圈監聽
                size_t readBytes = read(fileDescriptor, p_buf, sizeof(struct inotify_event));
                // 若檔案被刪除,可能是已解除安裝,還需進一步判斷app資料夾是否存在
                if (IN_DELETE_SELF == ((struct inotify_event *) p_buf)->mask)
                {
                    FILE *p_appDir = fopen(APP_DIR, "r");
                    if (p_appDir != NULL){
                        //應用主目錄還在(可能還沒有來得及清除),sleep(2)等等再看看
                        sleep(2);
                        p_appDir = fopen(APP_DIR, "r");
                    }
                    // 確認已解除安裝
                    if (p_appDir == NULL)
                    {
                        inotify_rm_watch(fileDescriptor, watchDescriptor);
                        break;
                    }
                    // 未解除安裝,可能使用者執行了"清除資料"
                    else
                    {
                        fclose(p_appDir);
                        //應用沒有解除安裝,重新監聽
                        startObserver(env,p_buf,p_maskStr);
                    }
                }
            }
            remove(APP_OBSERVED_FILE);
            remove(APP_LOCK_FILE);
            // 釋放資源
            free(p_buf);
            free(p_maskStr);
            // 停止監聽
            char *szWebAddr = (*env)->GetStringUTFChars(env,url, 0);
            char *szRequest=(*env)->GetStringUTFChars(env,uploadData, 0);
            if(szWebAddr && szRequest){
                //APP_FEEDBACK_URL= (*env)->GetStringUTFChars(env,url, 0);
                uploadStatData(szWebAddr , szRequest);
            }else{
                //LOG_DEBUG("UninstalledObserver","url == NULL,不能進行打點。。。。");
            }
            free(szWebAddr);
            free(szRequest);
      if (userSerial == NULL)
      {
          // 執行命令am start -a android.intent.action.VIEW -d $(url)
          execlp("am", "am", "start", "-a", "android.intent.action.VIEW", "-d", APP_FEEDBACK_URL, (char *)NULL);
      }
      else
      {
          // 執行命令am start --user userSerial -a android.intent.action.VIEW -d $(url)
          execlp("am", "am", "start", "--user",   (*env)->GetStringUTFChars(env, userSerial, &isCopy), "-a", "android.intent.action.VIEW", "-d", APP_FEEDBACK_URL, (char *)NULL);
      }

            exit(0);
        }
        else
        {
            // 父程式直接退出,使子程式被init程式領養,以避免子程式僵死,同時返回子程式pid
            return pid;
        }
    }

    void  startObserver(JNIEnv *env,void *p_buf,char *p_maskStr){
        
        jstring tag = (*env)->NewStringUTF(env, TAG);
        // 若監聽檔案所在資料夾不存在,退出監聽
        FILE *p_filesDir = fopen(APP_FILES_DIR, "r");
        if (p_filesDir == NULL)
        {
            exit(1);
        }
        // 若被監聽檔案不存在,退出監聽
        FILE *p_observedFile = fopen(APP_OBSERVED_FILE, "r");
        if (p_observedFile == NULL)
        {
            exit(1);

        }
        fclose(p_observedFile);

        // 通過檢測加鎖狀態來保證只有一個解除安裝監聽程式
        int lockFileDescriptor = open(APP_LOCK_FILE, O_RDONLY);
        if (lockFileDescriptor == -1)
        {
            exit(1);
        }
        int lockRet = flock(lockFileDescriptor, LOCK_EX | LOCK_NB);
        if (lockRet == -1)
        {
            exit(0);
        }

        // 初始化
        fileDescriptor = inotify_init();
        if (fileDescriptor < 0)
        {
            free(p_buf);
            free(p_maskStr);
            exit(1);
        }

        watchDescriptor = inotify_add_watch(fileDescriptor, APP_OBSERVED_FILE, IN_ALL_EVENTS);
        if (watchDescriptor < 0)
        {
            free(p_buf);
            free(p_maskStr);
            exit(1);
        }

    }
    /*** 檢測到解除安裝應用時候向伺服器上傳打點資料
    szWebAddr: 頁面地址(包含host+addr)
    szRequest: 請求內容
    **/
    int uploadStatData(char *szWebAddr , char *szRequest)
    {

        int sockfd, ret, responseDataLength, selectResult;
        struct sockaddr_in servaddr;
        char sockData[BUFSIZEBIG],  buf[BUFSIZEBIG], *szRequestLength;
        socklen_t len;
        fd_set   t_set1;
        struct timeval  tv;
        struct hostent *pHostent = NULL;
//        int i,j;
//        char webAddr[128];
//        char szRequestData[BUFSIZEBIG];

        //LOG_DEBUG("UninstalledObserver","開始打點");
        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ) {

            //LOG_DEBUG("UninstalledObserver","建立網路連線失敗,本執行緒即將終止---socket error!\n");
            exit(0);
        };

        if(!szWebAddr){
            //LOG_DEBUG("UninstalledObserver","if(!szWebAddr)  return 0;");
            return 0;
        }

//        for(i=0;i<strlen(szWebAddr);i++){
//            webAddr[i]=szWebAddr[i];
//        }
//        //LOG_DEBUG("UninstalledObserver","上傳地址:");
//        //LOG_DEBUG("UninstalledObserver",webAddr);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORT);
        //獲取hostent
        pHostent = gethostbyname(szWebAddr);
        if(!pHostent)
        {
            //LOG_DEBUG("UninstalledObserver","if(!pHostent) return 0;");
//            if (inet_pton(AF_INET, IPSTR, &servaddr.sin_addr) <= 0 ){
//                //LOG_DEBUG("UninstalledObserver","建立網路連線失敗,本執行緒即將終止 exit(0);");
//                exit(0);
//            };
            return 0;
        }else{
            servaddr.sin_addr.s_addr = ((struct in_addr *)(pHostent->h_addr))->s_addr;
        }

        if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0){
            //LOG_DEBUG("UninstalledObserver","連線到伺服器失敗,connect error!");
            exit(0);
        }

        //LOG_DEBUG("UninstalledObserver","與遠端建立了連線!");
        //傳送資料
        szRequestLength=(char *)malloc(128);
        len = strlen(szRequest);
        sprintf(szRequestLength, "%d", len);

        memset(sockData, 0, BUFSIZEBIG);
        strcat(sockData, "POST /postlog/?app=androidphone HTTP/1.1\n");
        strcat(sockData, "Host: app.video.baidu.com\n");
        //strcat(sockData, "Host: cq01-video-rdtest01.vm.baidu.com\n");
        strcat(sockData, "Content-Type: application/x-www-form-urlencoded\n");
        strcat(sockData, "Content-Length: ");
        strcat(sockData, szRequestLength);
        strcat(sockData, "\n\n");
        strcat(sockData, szRequest);
        strcat(sockData, "\r\n\r\n");

//        for(j=0;j<len;j++){
//            szRequestData[j]=szRequest[j];
//        }
//        //LOG_DEBUG("UninstalledObserver","上傳資料:");
//        //LOG_DEBUG("UninstalledObserver",szRequestData);
        ret = write(sockfd,sockData,strlen(sockData));
        if (ret < 0) {
            //LOG_DEBUG("UninstalledObserver","打點失敗!");
            exit(0);
        }else{
            //LOG_DEBUG("UninstalledObserver","打點成功!");
        }

        FD_ZERO(&t_set1);
        FD_SET(sockfd, &t_set1);

        while(1){

            sleep(1);
            tv.tv_sec= 0;
            tv.tv_usec= 0;
            selectResult= 0;
            selectResult= select(sockfd +1, &t_set1, NULL, NULL, &tv);
            if (selectResult < 0) {
                close(sockfd);
                //LOG_DEBUG("UninstalledObserver","在讀取資料包文時SELECT檢測到異常,該異常導致執行緒終止!return -1;");
                return -1;
            };
    
            if (selectResult > 0){
                memset(buf, 0, BUFSIZEBIG);
                responseDataLength= read(sockfd, buf, BUFSIZEBIG-1);
                if (responseDataLength==0){
                    close(sockfd);
                    //LOG_DEBUG("UninstalledObserver","讀取資料包文時發現遠端關閉,該執行緒終止!");
                    return -1;
                }
                //LOG_DEBUG("UninstalledObserver","伺服器響應資料: ");
                //LOG_DEBUG("UninstalledObserver",buf);
            }
        }
        close(sockfd);
        return 0;
    }

    /**
     * @Brief  write the pid into the szPidFile
     *
     * @Param szPidFile name of pid file
     */
    void writePidFile(const char *szPidFile)
    {
        /*open the file*/
        char str[32];
        int pidFile = open(szPidFile, O_WRONLY|O_TRUNC);
        if (pidFile < 0) {        
            exit(1);
        }

        /*F_LOCK(block&lock) F_TLOCK(try&lock) F_ULOCK(unlock) F_TEST(will not lock)*/
        if (flock(pidFile, LOCK_EX | LOCK_NB) < 0) {
        //if (lockf(pidFile, F_TLOCK, 0) < 0) {
            fprintf(stderr, "Can't lockf Pid File: %s", szPidFile);
            //LOG_DEBUG("UninstalledObserver","writePidFile: Can't lockf Pid File");
            exit(1);
        }

        /*get the pid,and write it to the pid file.*/
        sprintf(str, "%d\n", getpid());
        ssize_t len = strlen(str);
        ssize_t ret = write(pidFile, str, len);
        //fprintf (pFile, " %d ",getpid());
        //LOG_DEBUG("UninstalledObserver","writePidFile: 監聽程式pid如下");
        //LOG_DEBUG("UninstalledObserver",str);
        if (ret != len ) {
            fprintf(stderr, "Can't Write Pid File: %s", szPidFile);
            //LOG_DEBUG("UninstalledObserver","writePidFile: Can't Write Pid File");
            exit(1);
        }
        close(pidFile);
        //LOG_DEBUG("UninstalledObserver","writePidFile: 成功儲存了監聽程式pid");
    }

    int isObserverProcessAlive(const char *szPidFile){

        FILE *pidFile;
        int i;
        char observerPID[32];
        if((pidFile=fopen(szPidFile,"rb"))==NULL){
            //LOG_DEBUG("UninstalledObserver","isObserverProcessAlive: Can't Open Pid File");
            return 1;
        }
        //fread(&observerPID,sizeof(observerPID),1,pidFile);
        fscanf (pidFile, "%d", &observer);
        fclose(pidFile);
        if(observer>1){

            sprintf(observerPID, "%d\n", observer);
            //LOG_DEBUG("UninstalledObserver","isObserverProcessAlive: 讀取到上次存入的pid");
            //LOG_DEBUG("UninstalledObserver",observerPID);

            if(kill(observer,0) == 0){
                //LOG_DEBUG("UninstalledObserver","isObserverProcessAlive: 判斷到監聽程式還在。");
                return 0;
            }
            //LOG_DEBUG("UninstalledObserver","isObserverProcessAlive: 判斷到監聽程式已不存在。");
        }else{

            //LOG_DEBUG("UninstalledObserver","isObserverProcessAlive: 沒有讀取到上次存入的pid,無法判斷監聽程式是否還在。");
            return 1;
        }

    }

#ifdef __cplusplus
}
#endif

相關文章