(4.6.17.4)进程保活(三:JNI层初探):单进程单向循环式启动服务

前言

我们先来看看Android4.4的源码,ActivityManagerService(源码/frameworks/base/services/core/Java/com/Android/server/am/ActivityManagerService.java)是如何关闭在应用退出后清理内存的:

Process.killProcessQuiet(pid);  

应用退出后,ActivityManagerService就把主进程给杀死了,但是,在Android5.0中,ActivityManagerService却是这样处理的:

Process.killProcessQuiet(app.pid);  
Process.killProcessGroup(app.info.uid, app.pid);  

就差了一句话,却差别很大。Android5.0在应用退出后,ActivityManagerService不仅把主进程给杀死,另外把主进程所属的进程组一并杀死,这样一来,由于子进程和主进程在同一进程组,子进程在做的事情,也就停止了…要不怎么说Android5.0在安全方面做了很多更新呢…
那么,有没有办法让子进程脱离出来,不要受到主进程的影响,当然也是可以的—— fork两次,第一个子进程独立到新的会话中,并在第二个子进程启动服务

方法一

技术关键点:开启native子进程,循环间隔的不停去启动下服务,也不判断服务是否die了。而且,如果die了,但是间隔还没到,还是不会启动服务

保活层次:基本没有实现服务和进程的相互监听,就是简单的native循环启动服务;
没有“a死,b才立即启动a;b死,a才立即启动b”的保活概念
http://download.csdn.net/detail/fei20121106/9584301

  • 结论:单杀可以杀死,force close 5.0以上无效,5.0以下部分手机无效,第三方软件下无效,且无法保证实时常驻,且费电

    • 这是要建立在保证c进程不挂的基础上,才能轮询,但是就目前来看,只有5.0以下的非国产机才会有这样的漏洞。也就是说在force close的时候,系统忽略c进程的存在,5.0以上包括5.0的哪怕源生系统也会连同c进程一起清理掉,国产机就更不用说了。就算是这样,在5.0以下的非国产机上,如果安装了获取root权限的360\cm的话,也是可以直接清理掉,也就是说会失效
    • 而且他不但不算守护,而且还是单向的,也就是说只能a保b,b保不了a;a保b也不是在b死了立刻拉起来,要等到了时间才会去拉。

配置

  • 集成library
    这里写图片描述

调用

Daemon.run(上下文, 要守护的服务, 时间间隔);

示例:

public class DaemonService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        Daemon.run(this, DaemonService.class, Daemon.INTERVAL_ONE_MINUTE * 2);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        /* do something here */
        return super.onStartCommand(intent, flags, startId);
    }
}

JNI代码分析

/*
 * File        : daemon.c
 * Author      : Vincent Cheung
 * Date        : Jan. 20, 2015
 * Description : This is used as process daemon.
 *
 * Copyright (C) Vincent Chueng<coolingfall@gmail.com>
 *
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/system_properties.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "common.h"

#define LOG_TAG         "Daemon"
#define MAXFILE         3
#define SLEEP_INTERVAL  2 * 60

volatile int sig_running = 1;

/* signal term handler */
static void sigterm_handler(int signo)
{
    LOGD(LOG_TAG, "handle signal: %d ", signo);
    sig_running = 0;
}

/* start daemon service */
//在新的子进程中启动服务
static void start_service(char *package_name, char *service_name)
{
    /* get the sdk version */
    int version = get_version();

    pid_t pid;

    //父进程从fork()返回时的返回值是子进程的进程号,非零正数;
    //而子进程从fork()返回(严格来说子进程是从这里开始的)时的返回值是0。
    //所以通过判断p的值是否为0就能知道现在运行的是父进程还是子进程。

    if ((pid = fork()) < 0) //第二次fork
    {
        exit(EXIT_SUCCESS);  //fork失败
    }
    else if (pid == 0)  //如果子进程,pid返回值是0 ,进入该逻辑[第二个子进程]
    {
        if (package_name == NULL || service_name == NULL)
        {
            LOGE(LOG_TAG, "package name or service name is null");
            return;
        }

        /****启动android层面的service** START**/
        char *p_name = str_stitching(package_name, "/");
        char *s_name = str_stitching(p_name, service_name);
        LOGD(LOG_TAG, "service: %s", s_name);

        if (version >= 17 || version == 0)
        {
            int ret = execlp("am", "am", "startservice",
                        "--user", "0", "-n", s_name, (char *) NULL);
            LOGD(LOG_TAG, "result %d", ret);
        }
        else
        {
            execlp("am", "am", "startservice", "-n", s_name, (char *) NULL);
        }
        /****启动android层面的service**END**/

        LOGD(LOG_TAG , "exit start-service child process");
        exit(EXIT_SUCCESS);
    }
    else//如果是父进程,返回值为pid=子进程id号  【父进程==第一个子进程】
    {
    //那么怎么样才能实现双向守护呢?
    //首先我们想到的是fork这个函数,他会创建一个子进程,
    //然后在父进程中调用waitpid()这个函数,这是一个阻塞函数,
    //父进程会一直wait到子进程挂掉,才会继续向下执行,利用这个机制
    //我们可以在主进程的c层fork一个子进程,然后父进程就可以监听到子进程的死亡,死亡的时候再重启子进程。

        waitpid(pid, NULL, 0); //等待子进程结束,才会进入循环,也就是开始时间间隔,进入下一次循环
    }
}

int main(int argc, char *argv[])
{
    int i;
    pid_t pid;
    char *package_name = NULL;
    char *service_name = NULL;
    char *daemon_file_dir = NULL;
    int interval = SLEEP_INTERVAL;

    LOGI(LOG_TAG, "Copyright (c) 2015-2016, Vincent Cheung<coolingfall@gmail.com>");

    if (argc < 7)
    {
        LOGE(LOG_TAG, "usage: %s -p package-name -s "
         "daemon-service-name -t interval-time", argv[0]);
        return;
    }

    for (i = 0; i < argc; i ++) //读命令参数
    {
        if (!strcmp("-p", argv[i]))
        {
            package_name = argv[i + 1];
            LOGD(LOG_TAG, "package name: %s", package_name);
        }

        if (!strcmp("-s", argv[i]))
        {
            service_name = argv[i + 1];
            LOGD(LOG_TAG, "service name: %s", service_name);
        }

        if (!strcmp("-t", argv[i]))
        {
            interval = atoi(argv[i + 1]);
            LOGD(LOG_TAG, "interval: %d", interval);
        }
    }

    /* package name and service name should not be null */
    if (package_name == NULL || service_name == NULL)
    {
        LOGE(LOG_TAG, "package name or service name is null");
        return;
    }

    if ((pid = fork()) < 0)    //第一次fork
    {
        exit(EXIT_SUCCESS);   //fork失败
    }
    else if (pid == 0)   //如果子进程,pid返回值是0 ,进入该逻辑【第一个子进程】
    {
        /* add signal */
        //KILL命令的默认不带参数发送的信号就是SIGTERM.让程序有好的退出
        //注册接听到SIGTERM信号时,使用sigterm_handler函数处理
        signal(SIGTERM, sigterm_handler);//用于实现子进程的关闭退出

        //如果parent和child运行在同一个session里,parent是会话(session)的领头进程,
        //parent进程作为会话的领头进程,如果exit结束执行的话,那么子进程会成为孤儿进程,并被init收养。
        //执行setsid()之后,child将重新获得一个新的会话(session)id。
        //parent退出之后,将不会影响到child了。

        //setsid的调用者不能是进程组组长(group leader)
        //setsid()调用成功后,返回新的会话的ID
        //调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离。
        setsid();
        /* change work directory */
        //修改进程工作目录为根目录,chdir(“/”)
        chdir("/");

        for (i = 0; i < MAXFILE; i ++)
        {
            close(i);
        }

        /* find pid by name and kill them */
        int pid_list[100];
        int total_num = find_pid_by_name(argv[0], pid_list);
        LOGD(LOG_TAG, "total num %d", total_num);
        for (i = 0; i < total_num; i ++)
        {
            int retval = 0;
            int daemon_pid = pid_list[i];
            if (daemon_pid > 1 && daemon_pid != getpid())
            {
                retval = kill(daemon_pid, SIGTERM); //【!!!】释放信号,关闭之前开启的子进程
                if (!retval)
                {
                    LOGD(LOG_TAG, "kill daemon process success: %d", daemon_pid);
                }
                else
                {
                    LOGD(LOG_TAG, "kill daemon process %d fail: %s", daemon_pid, strerror(errno));
                    exit(EXIT_SUCCESS);
                }
            }
        }

        LOGD(LOG_TAG, "child process fork ok, daemon start: %d", getpid());

        while(sig_running)
        {  //过一段时间间隔去启动下服务
            interval = interval < SLEEP_INTERVAL ? SLEEP_INTERVAL : interval;
            select_sleep(interval, 0);

            LOGD(LOG_TAG, "check the service once, interval: %d", interval);

            /* start service */
            //使用了waitpid(),第一个子进程会阻塞到start_service启动的第二个进程结束,才开始新循环
            //避免了不断重复创建进程
            start_service(package_name, service_name);
        }

        exit(EXIT_SUCCESS);//【!!!】循环被破坏后,自己=第一个子进程退出,保证了进程会被回收
    }
    else
    {//如果是父进程,返回值为pid=子进程id号
        /* parent process */
        exit(EXIT_SUCCESS); //父进程直接退出
    }
}

方法二

单进程单向循环式启动

主进程fork进程1,进程1独立出来到新的会话组,fork进程2;进程1退出,进程2开启线程,线程循环启动服务
Android 通过JNI实现守护进程,使Service服务不被杀死

JNI

#include <string.h>
#include <jni.h>
#include <unistd.h>
#include <stdio.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) {
    while(1){   //循环式的检测和启动服务
        check_and_restart_service(srvname);
        sleep(4);//空闲间隔
    }
}

char* a;

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

    /**
     * 第一次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 { //  第一个子进程
        LOGI("first fork(): I'am child pid=%d", getpid());
        //int setsid = setsid();
        LOGI("first fork(): setsid=%d", setsid());
        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("/");
            //step 5:关闭不需要的从父进程继承过来的文件描述符。
            if (r.rlim_max == RLIM_INFINITY) {
                r.rlim_max = 1024;
            }
            int i;
            for (i = 0; i < r.rlim_max; i++) {
                close(i);
            }

            umask(0);
            //创建线程(实际上就是确定调用该线程函数的入口点),在线程创建以后,就开始运行相关的线程函数
            //thread:线程标识符;     attr:线程属性设置;    start_routine:线程函数的起始地址; arg:传递给start_routine的参数;
            //返回值:成功,返回0;出错,返回-1。
            ret = pthread_create(&id, NULL, (void *) thread, srvname);
            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("当前所在的进程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_yyh_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_yyh_fork_NativeRuntime_findProcess(JNIEnv* env,
        jobject thiz, jstring cchrptr_ProcessName) {
    char * rtn = jstringTostring(env, cchrptr_ProcessName);
    LOGI("Java_com_yyh_fork_NativeRuntime_findProcess run....ProcessName:%s", rtn);
    //return 1;
    return GetPIDbyName_implements(rtn, 0, 0); //大小写不敏感 sub串匹配
}

/**
 * 启动Service
 */
void Java_com_yyh_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_yyh_fork_NativeRuntime_startService run....ProcessName:%s", rtn);
    a = rtn;
    start(1, rtn, sd);
}

/**
 * 关闭Service
 */
void Java_com_yyh_fork_NativeRuntime_stopService() {
    exit(0);
}

/**
 * 启动Activity
 */
void Java_com_yyh_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;
}

使用

C/C++端关键的部分主要是以上这些,自然而然,Java端还得配合执行。
首先来看一下C/C++代码编译完的so库的加载类,以及native的调用:

package com.yyh.fork;  

import java.io.DataInputStream;  
import java.io.DataOutputStream;  
import java.io.File;  

public class NativeRuntime {  

    private static NativeRuntime theInstance = null;  

    private NativeRuntime() {  

    }  

    public static NativeRuntime getInstance() {  
        if (theInstance == null)  
            theInstance = new NativeRuntime();  
        return theInstance;  
    }  

    /** 
     * RunExecutable 启动一个可自行的lib*.so文件 
     * @date 2016-1-18 下午8:22:28 
     * @param pacaageName 
     * @param filename 
     * @param alias 别名 
     * @param args 参数 
     * @return 
     */  
    public String RunExecutable(String pacaageName, String filename, String alias, String args) {  
        String path = "/data/data/" + pacaageName;  
        String cmd1 = path + "/lib/" + filename;  
        String cmd2 = path + "/" + alias;  
        String cmd2_a1 = path + "/" + alias + " " + args;  
        String cmd3 = "chmod 777 " + cmd2;  
        String cmd4 = "dd if=" + cmd1 + " of=" + cmd2;  
        StringBuffer sb_result = new StringBuffer();  

        if (!new File("/data/data/" + alias).exists()) {  
            RunLocalUserCommand(pacaageName, cmd4, sb_result); // 拷贝lib/libtest.so到上一层目录,同时命名为test.  
            sb_result.append(";");  
        }  
        RunLocalUserCommand(pacaageName, cmd3, sb_result); // 改变test的属性,让其变为可执行  
        sb_result.append(";");  
        RunLocalUserCommand(pacaageName, cmd2_a1, sb_result); // 执行test程序.  
        sb_result.append(";");  
        return sb_result.toString();  
    }  

    /** 
     * 执行本地用户命令 
     * @date 2016-1-18 下午8:23:01 
     * @param pacaageName 
     * @param command 
     * @param sb_out_Result 
     * @return 
     */  
    public boolean RunLocalUserCommand(String pacaageName, String command, StringBuffer sb_out_Result) {  
        Process process = null;  
        try {  
            process = Runtime.getRuntime().exec("sh"); // 获得shell进程  
            DataInputStream inputStream = new DataInputStream(process.getInputStream());  
            DataOutputStream outputStream = new DataOutputStream(process.getOutputStream());  
            outputStream.writeBytes("cd /data/data/" + pacaageName + "\n"); // 保证在command在自己的数据目录里执行,才有权限写文件到当前目录  
            outputStream.writeBytes(command + " &\n"); // 让程序在后台运行,前台马上返回  
            outputStream.writeBytes("exit\n");  
            outputStream.flush();  
            process.waitFor();  
            byte[] buffer = new byte[inputStream.available()];  
            inputStream.read(buffer);  
            String s = new String(buffer);  
            if (sb_out_Result != null)  
                sb_out_Result.append("CMD Result:\n" + s);  
        } catch (Exception e) {  
            if (sb_out_Result != null)  
                sb_out_Result.append("Exception:" + e.getMessage());  
            return false;  
        }  
        return true;  
    }  

    public native void startActivity(String compname);  

    public native String stringFromJNI();  

    public native void startService(String srvname, String sdpath);  

    public native int findProcess(String packname);  

    public native int stopService();  

    static {  
        try {  
            System.loadLibrary("helper"); // 加载so库  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
    }  

}  

然后,我们在收到开机广播后,启动该服务。

package com.yyh.activity;  

import android.content.BroadcastReceiver;  
import android.content.Context;  
import android.content.Intent;  
import android.util.Log;  

import com.yyh.fork.NativeRuntime;  
import com.yyh.utils.FileUtils;  
public class PhoneStatReceiver extends BroadcastReceiver {  

    private String TAG = "tag";  

    @Override  
    public void onReceive(Context context, Intent intent) {  
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
            Log.i(TAG, "手机开机了~~");  
            NativeRuntime.getInstance().startService(context.getPackageName() + "/com.yyh.service.HostMonitor", FileUtils.createRootPath());  
        } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
        }  
    }  


}  

Service服务里面,就可以做该做的事情。

package com.yyh.service;  

import android.app.Service;  
import android.content.Intent;  
import android.os.IBinder;  
import android.util.Log;  

public class HostMonitor extends Service {  

    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.i("daemon_java", "HostMonitor: onCreate! I can not be Killed!");  
    }  

    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.i("daemon_java", "HostMonitor: onStartCommand! I can not be Killed!");  
        return super.onStartCommand(intent, flags, startId);  
    }  

    @Override  
    public IBinder onBind(Intent arg0) {  
        return null;  
    }  
}  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值