Android Service后台进程守护

看了很多资料,说是在android java层实现服务自启动,但是亲测,无论怎么做都达不到自启动。

后面看了网上一些资料,从linux层面着手,感觉可行,不过很多资料都采用轮询的方法去启动服务,个人感觉轮询从耗电等各方面考虑并不是最优方案,因为如果可以检测到服务挂了然后直接启动服务可能更节省性能,而android本身提供了这样的机制,service死掉后会调用生命周期中的onDestroy进行销毁。因此,我们就可以通过这样的方法在依靠linux下孤立的子进程存活的情况下去调用启动服务命令。

基本流程:


父进程fork后产生孤立的子进程A, A启动服务进程s, s发现自己将要销毁时依靠A进程调用启动s的命令。

c关键代码:

#include <stdio.h>
#include <string.h>
#include <jni.h>
#include <unistd.h>
#include <stdlib.h>

#include <sys/resource.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <pthread.h>

#define PROC_DIRECTORY "/proc/"
#define CASE_SENSITIVE    1
#define CASE_INSENSITIVE  0
#define EXACT_MATCH       1
#define INEXACT_MATCH     0
#define MAX_LINE_LEN 5

#include <android/log.h>
#define  TAG    "daemon"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#define LOG "Helper"

void thread(char* srvname) {
    check_and_restart_service(srvname);
}

char* a;
char* srvn;

/**
 * srvname  服务名
 * sd 之前创建子进程的pid写入的文件路径
 */
int start(int argc, char* srvname, char* sd) {
    pthread_t id;
    int ret;
    struct rlimit r;
    srvn = srvname;

    /**
     * 第一次fork的作用是让shell认为本条命令已经终止,不用挂在终端输入上。
     * 还有一个作用是为后面setsid服务。setsid的调用者不能是进程组组长(group leader)。
     * 此时父进程是进程组组长。
     */
    int pid = fork();
    LOGI("fork pid: %d", pid);
    if (pid < 0) {
        LOGI("first fork() error pid %d,so exit", pid);
        exit(0);
    } else if (pid != 0) {
        LOGI("first fork(): I'am father pid=%d", getpid());
        //exit(0);
    } else { //  第一个子进程
        umask(0); //使用umask修改文件的屏蔽字,为文件赋予跟多的权限,因为继承来的文件可能某些权限被屏蔽,从而失去某些功能,如读写

        int pid = fork();
        if (pid == 0) { // 第二个子进程
            FILE  *fp;
            sprintf(sd,"%s/pid",sd);
            if((fp=fopen(sd,"a"))==NULL) {//打开文件 没有就创建
                LOGI("%s文件还未创建!",sd);
                ftruncate(fp, 0);
                lseek(fp, 0, SEEK_SET);
            }
            fclose(fp);
            fp=fopen(sd,"rw");
            if(fp>0){
                char buff1[6];
                int p = 0;
                memset(buff1,0,sizeof(buff1));
                fseek(fp,0,SEEK_SET);
                fgets(buff1,6,fp);  //读取一行
                LOGI("读取的进程号:%s",buff1);
                if(strlen(buff1)>1){ // 有值

                    kill(atoi(buff1), SIGTERM);
                    LOGI("杀死进程,pid=%d",atoi(buff1));
                }
            }
            fclose(fp);
            fp=fopen(sd,"w");
            char buff[100];
            int k = 3;
            if(fp>0){
                sprintf(buff,"%lu",getpid());
                fprintf(fp,"%s\n",buff); // 把进程号写入文件
                LOGI("写入。。。。。");
            }
            fclose(fp);
            fflush(fp);

            LOGI("step 2 I'am child-child pid=%d", getpid());
            //step 4:修改进程工作目录为根目录,chdir(“/”).
            chdir("/");
            LOGI("after step 4 ...11133.", "");
            //step 5:关闭不需要的从父进程继承过来的文件描述符。
            LOGI("after step 5 ...1111.", "");
            umask(0);
            // 关闭不必要的文件描述符
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);

            ret = pthread_create(&id, NULL, (void *) thread, srvname);
            LOGI("after step 5 ....222", "");
            if (ret != 0) {
                printf("Create pthread error!\n");
                exit(1);
            }
            int stdfd = open ("/dev/null", O_RDWR);
            dup2(stdfd, STDOUT_FILENO);
            dup2(stdfd, STDERR_FILENO);
        } else {
            exit(0);
        }
    }
    return 0;
}

/**
 * 执行命令
 */
void ExecuteCommandWithPopen(char* command, char* out_result,
                             int resultBufferSize) {
    FILE * fp;
    out_result[resultBufferSize - 1] = '\0';
    fp = popen(command, "r");
    if (fp) {
        fgets(out_result, resultBufferSize - 1, fp);
        out_result[resultBufferSize - 1] = '\0';
        pclose(fp);
    } else {
        LOGI("popen null,so exit");
        exit(0);
    }
}

/**
 * 检测服务,如果不存在服务则启动.
 * 通过am命令启动一个laucher服务,由laucher服务负责进行主服务的检测,laucher服务在检测后自动退出
 */
void check_and_restart_service(char* service) {
    LOGI("current process pid=",getpid());
    char cmdline[200];
    sprintf(cmdline, "am startservice --user 0 -n %s", service);
    char tmp[200];
    sprintf(tmp, "cmd=%s", cmdline);
    ExecuteCommandWithPopen(cmdline, tmp, 200);
    LOGI( tmp, LOG);
}

jstring stoJstring(JNIEnv* env, const char* pat) {
    jclass strClass = (*env)->FindClass(env, "Ljava/lang/String;");
    jmethodID ctorID = (*env)->GetMethodID(env, strClass, "<init>",
                                           "([BLjava/lang/String;)V");
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), (jbyte*) pat);
    jstring encoding = (*env)->NewStringUTF(env, "utf-8");
    return (jstring)(*env)->NewObject(env, strClass, ctorID, bytes, encoding);
}


/**
 * 判断是否是数字
 */
int IsNumeric(const char* ccharptr_CharacterList) {
    //LOGI("IsNumeric: test.cpp/main: argc=%d",ccharptr_CharacterList);
    for (; *ccharptr_CharacterList; ccharptr_CharacterList++)
        if (*ccharptr_CharacterList < '0' || *ccharptr_CharacterList > '9')
            return 0; // false
    return 1; // true
}

//intCaseSensitive=0大小写不敏感
int strcmp_Wrapper(const char *s1, const char *s2, int intCaseSensitive) {
    if (intCaseSensitive)
        return !strcmp(s1, s2);
    else
        return !strcasecmp(s1, s2);
}

//intCaseSensitive=0大小写不敏感
int strstr_Wrapper(const char* haystack, const char* needle,
                   int intCaseSensitive) {
    if (intCaseSensitive)
        return (int) strstr(haystack, needle);
    else
        return (int) strcasestr(haystack, needle);
}

/**
 * 通过进程名称获取pid
 */
pid_t GetPIDbyName_implements(const char* cchrptr_ProcessName,
                              int intCaseSensitiveness, int intExactMatch) {

    char chrarry_CommandLinePath[100];
    char chrarry_NameOfProcess[300];
    char* chrptr_StringToCompare = NULL;
    pid_t pid_ProcessIdentifier = (pid_t) - 1;
    struct dirent* de_DirEntity = NULL;
    DIR* dir_proc = NULL;

    int (*CompareFunction)(const char*, const char*, int);

    if (intExactMatch)
        CompareFunction = &strcmp_Wrapper;
    else
        CompareFunction = &strstr_Wrapper;

    dir_proc = opendir(PROC_DIRECTORY);
    if (dir_proc == NULL) {
        perror("Couldn't open the " PROC_DIRECTORY " directory");
        return (pid_t) - 2;
    }

    while ((de_DirEntity = readdir(dir_proc))) {
        if (de_DirEntity->d_type == DT_DIR) {

            if (IsNumeric(de_DirEntity->d_name)) {
                strcpy(chrarry_CommandLinePath, PROC_DIRECTORY);
                strcat(chrarry_CommandLinePath, de_DirEntity->d_name);
                strcat(chrarry_CommandLinePath, "/cmdline");
                FILE* fd_CmdLineFile = fopen(chrarry_CommandLinePath, "rt"); //open the file for reading text
                if (fd_CmdLineFile) {
                    LOGI("chrarry_NameOfProcess %s", chrarry_NameOfProcess);
                    fscanf(fd_CmdLineFile, "%s", chrarry_NameOfProcess); //read from /proc/<NR>/cmdline
                    fclose(fd_CmdLineFile); //close the file prior to exiting the routine

                    chrptr_StringToCompare = chrarry_NameOfProcess;
                    if (CompareFunction(chrptr_StringToCompare,
                                        cchrptr_ProcessName, intCaseSensitiveness)) {
                        pid_ProcessIdentifier = (pid_t) atoi(
                                de_DirEntity->d_name);
                        LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier);
                        closedir(dir_proc);
                        return pid_ProcessIdentifier;
                    }
                }
            }
        }
    }
    LOGI("processName=%d, pid=%d",cchrptr_ProcessName,pid_ProcessIdentifier);
    closedir(dir_proc);
    return pid_ProcessIdentifier;
}

/**
 * 检测服务,如果不存在服务则启动
 */
void check_and_restart_activity(char* service) {

    char cmdline[200];
    sprintf(cmdline, "am start -n %s", service);
    char tmp[200];
    sprintf(tmp, "cmd=%s", cmdline);
    ExecuteCommandWithPopen(cmdline, tmp, 200);
    LOGI( tmp, LOG);
}

/**
 * 返回ABI给Java
 */
jstring Java_com_daemonprocess_fork_NativeRuntime_stringFromJNI(JNIEnv* env, jobject thiz) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
    #if defined(__ARM_NEON__)
            #define ABI "armeabi-v7a/NEON"
         #else
            #define ABI "armeabi-v7a"
         #endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
    #define ABI "x86"
   #elif defined(__mips__)
      #define ABI "mips"
   #else
      #define ABI "unknown"
#endif
    return (*env)->NewStringUTF(env,
                                "Hello from JNI !  Compiled with ABI " ABI ".");
}

/**
 * jstring 转 String
 */
char* jstringTostring(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env, "utf-8");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
                                        "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
                                                           strencode);
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if (alen > 0) {
        rtn = (char*) malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba, 0);
    return rtn;
}
/**
 * 查找进程
 */
pid_t JNICALL Java_com_daemonprocess_fork_NativeRuntime_findProcess(JNIEnv* env,
jobject thiz, jstring cchrptr_ProcessName) {
    char * rtn = jstringTostring(env, cchrptr_ProcessName);
    LOGI("Java_com_daemonprocess_fork_NativeRuntime_findProcess run....ProcessName:%s", rtn);
    //return 1;
    return GetPIDbyName_implements(rtn, 0, 0); //大小写不敏感 sub串匹配
}
/**
 * 启动Service
 */
void Java_com_daemonprocess_fork_NativeRuntime_startService(JNIEnv* env, jobject thiz,
jstring cchrptr_ProcessName, jstring sdpath) {
    char * rtn = jstringTostring(env, cchrptr_ProcessName); // 得到进程名称
    char * sd = jstringTostring(env, sdpath);
    LOGI("Java_com_daemonprocess_fork_NativeRuntime_startService run....ProcessName:%s", rtn);
    a = rtn;
    start(1, rtn, sd);
}
/**
 * 关闭Service
 */
JNIEXPORT jint JNICALL Java_com_daemonprocess_fork_NativeRuntime_stopService(JNIEnv* env, jobject thiz) {
    LOGI("========exit", "");
    exit(0);
}
/**
 * service已经断开,重启服务
 */
JNIEXPORT void JNICALL Java_com_daemonprocess_fork_NativeRuntime_serviceHasDisconnection
(JNIEnv* env, jobject thiz){
if(srvn != NULL){
LOGI("========serviceHasDisconnection serviceprocess--%s", srvn);
check_and_restart_service(srvn);
}
}
/**
 * 启动Activity
 */
void Java_com_daemonprocess_fork_NativeRuntime_startActivity(JNIEnv* env, jobject thiz,
jstring activityName) {
    char * rtn = jstringTostring(env, activityName);
    LOGI("check_and_restart_activity run....activityName:%s", rtn);
    check_and_restart_activity(rtn);
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    JNIEnv* env = NULL;
    jint result = -1;

    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    return result;
    }
    LOGI("JNI_OnLoad ......");
    return JNI_VERSION_1_4;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值