让Adb.exe支持Monkey

作者: 雪山宝宝 | 来源:发表于2015-09-27 22:43 被阅读1140次

    最近老研究一些之前的东西,越来越怀旧了真是

    下载源码

    要改造adb.exe从思路来讲很简单,把android源码里面的adb.exe部分提取出来,改造再进行编译不就可以了?是的。但是这一块的源码在哪个模块呢?由哪些文件构成?我是不是要先下载所有的Android源码才找得到?现在google的网站被封?我还得先...... 技术就是这样,思路很简单的事情操作起来可能并不简单,需要大把的时间,这里给大家介绍一个资源

    android源码查找,可以根据文件名,类名,方法名的定义去查找到相应的文件(http://osxr.org/android/ident?)(Ver: 4.1.2_r2)

    有了该网站,就可以不用下载整个android源码了,但是我还是把一整套android源码下下来了,整个过程太过艰辛,感叹一下做技术人员不容易,和各种势力进行抗争啊真是。经过一番查找,下面为adb client所在的源码目录

    http://osxr.org/android/source/system/core/adb/

    到了这里,是不是要去找一些官方文档,该目录下的一些文档说明来进行编译,太纠结了,去github上找找吧,现在是共享时代

    https://github.com/t-mat/my_adb

    啰嗦了一堆,其实就是下载my_adb去进行改造,不要打我

    对源码进行改造

    **源码结构 **

    //入口 adb.c
    int main(int argc, char **argv)
     {
     #if ADB_HOST
         adb_sysdeps_init();
         adb_trace_init();
         D("Handling commandline()\n");
         return adb_commandline(argc - 1, argv + 1);
    #else
        if((argc > 1) && (!strcmp(argv[1],"recovery"))) {
            adb_device_banner = "recovery";
            recovery_mode = 1;
        }
    
        start_device_log();
        return adb_main(0, DEFAULT_ADB_PORT);
    #endif
     }
    

    我们执行adb.exe时,会进入到if ture里面的分支,进入到adb_commondline函数

    int adb_commandline(int argc, char **argv)
    {
    ...
    if(!strcmp(argv[0], "devices")) {
            char *tmp;
            snprintf(buf, sizeof buf, "host:%s", argv[0]);
            tmp = adb_query(buf);
            if(tmp) {
                printf("List of devices attached \n");
                printf("%s\n", tmp);
                return 0;
            } else {
                return 1;
            }
        }
    
        if(!strcmp(argv[0], "connect")) {
            char *tmp;
            if (argc != 2) {
                fprintf(stderr, "Usage: adb connect <host>[:<port>]\n");
                return 1;
            }
            snprintf(buf, sizeof buf, "host:connect:%s", argv[1]);
            tmp = adb_query(buf);
            if(tmp) {
                printf("%s\n", tmp);
                return 0;
            } else {
                return 1;
            }
        }
        ...
    }
    

    该函数里面的功能就是常规的解析命令行参数,然后执行对应的功能,上面的代码展示出来的是常见的两个功能,显示设备列表和连接到某台设备

    OK,不往下分析了,先分析一下我们需要加的功能吧

    需要改造的功能

    通过上一篇文章的分析可以得出有两个功能需要改进:adb.exe中无法传送utf-8格式字符串的问题增加传送monkey指令的功能

    adb.exe中无法传送utf-8格式字符串的问题

    该问题网上是有现成的解决方案的,思路上只需要在发送buffer的时候,把gbk转成utf-8即可,adb daemon 是识别utf-8格式的,所以原生的adb传送gbk格式,中文就会显示不出来。
    cmd默认字符编码是gbk,而且vs2013里面默认的文件编码也是gbk,如果把vs2013里面的源码文件改成utf-8格式的,是否可以不更改adb的源码,就可以用?我没试过,有兴趣的朋友可以测试一下,从原理上来讲是可以的。

    增加传送monkey指令的功能

    这个功能是需要和本地的adb server进行TCP交互的,还记得上一篇文章的图吗?

    Adb功能结构图

    执行monkey命令流程流程是这样的,一次连接可能会执行很多次命令

    Monkey命令执行流程

    socket层(TCP传输层)

    增加传送monkey指令的功能

    我们看一下socket层的代码是否需要我们自己添加

    char *adb_query(const char *service)
    {
        char buf[5];
        unsigned n;
        char *tmp;
        int fd; //+
    
        D("adb_query: %s\n", service);
    //- int fd = adb_connect(service);
        fd = adb_connect(service);
        if(fd < 0) {
            fprintf(stderr,"error: %s\n", __adb_error);
            return 0;
        }
    
        if(readx(fd, buf, 4)) goto oops;
    ...
    }
    
    int adb_connect(const char *service)
    {
        // first query the adb server's version
        int fd = _adb_connect("host:version");
    
        if(fd == -2) {
            fprintf(stderr, "* daemon not running. *\n");
            return -1;
    
            fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
                    __adb_server_port);
        start_server:
        ...
    }
    

    adb client的命令有一部分是通过adb_query函数进行发送,里面会用到adb_connect,返回socket实例,再通过该socket读取到返回的内容进行输出

    unsigned char* adb_monkey_query(const char *service, int mFd)
    {
        char buf[5];
        unsigned n;
        int fd; //+
        unsigned char* outRel = NULL;
    
        D("adb_query: %s\n", service);
        //- int fd = adb_connect(service);
        fd = _adb_monkey_connect(service,mFd,&outRel);
        if(fd < 0) {
            fprintf(stderr,"error: %s\n", __adb_error);
        }
        return outRel;
    }
    
    int adb_monkey_exec(const char *service, int mFd)
    {
        char buf[5];
        unsigned n;
        int fd; //+
        unsigned char* outRel = NULL;
    
        D("adb_query: %s\n", service);
        //- int fd = adb_connect(service);
        fd = _adb_monkey_connect(service,mFd,&outRel);
        if (outRel!=NULL)
        {
            free(outRel);
        }
        if(fd < 0) {
            fprintf(stderr,"error: %s\n", __adb_error);
            return 0;
        }
        return 1;
    }
    
    int _adb_monkey_connect(const char *service, int mFd, unsigned char** outRel)
    {
        char tmp[5];
        int len;
    
        D("_adb_monkey_connect: %s\n", service);
        len = strlen(service);
        if((len < 1) || (len > 1024)) {
            strcpy(__adb_error, "service name too long");
            return -1;
        }
        if (mFd<=0)
        {
            mFd = socket_loopback_client(__adb_monkey_server_port, SOCK_STREAM);
        }
        if(mFd < 0) {
            strcpy(__adb_error, "cannot connect to monkey port");
            return -2;
        }
    
        if(writex(mFd, service, len)) {
            strcpy(__adb_error, "write monkey cmd failure during connection");
            adb_close(mFd);
            return -1;
        }
    
        if(adb_monkey_status(mFd,outRel)) {
            strcpy(__adb_error, "monkey cmd return err");
            //adb_close(mFd);
            return -1;
        }
    
        return mFd;
    }
    

    通过上面的代码可以看出,我增加了adb_monkey_query adb_monkey_exec 和 adb_monkey_connect,其实query和exec都是用到了connect函数,只是一个有返回,一个没有返回而已,底层adb_monkey_connect怎么实现的,其实和_adb_connect类似

    int _adb_monkey_connect(const char *service, int mFd, unsigned char** outRel)
    {
        char tmp[5];
        int len;
    
        D("_adb_monkey_connect: %s\n", service);
        len = strlen(service);
        if((len < 1) || (len > 1024)) {
            strcpy(__adb_error, "service name too long");
            return -1;
        }
        if (mFd<=0)
        {
            mFd = socket_loopback_client(__adb_monkey_server_port, SOCK_STREAM);
        }
        if(mFd < 0) {
            strcpy(__adb_error, "cannot connect to monkey port");
            return -2;
        }
    
        if(writex(mFd, service, len)) {
            strcpy(__adb_error, "write monkey cmd failure during connection");
            adb_close(mFd);
            return -1;
        }
    
        if(adb_monkey_status(mFd,outRel)) {
            strcpy(__adb_error, "monkey cmd return err");
            //adb_close(mFd);
            return -1;
        }
    
        return mFd;
    }
    
    int adb_monkey_status(int fd,unsigned char** rel)
    {
        unsigned char tbuf[1];
        int len,alllen=0;
        int fail = 0;
        *rel = NULL;
        while(fd >= 0) {
            len = adb_read(fd, tbuf, 1);
            if (len == 0)
            {//读取出错
                fail = 1;
            }
            if(len == 0 || tbuf[0]=='\n') {
                break;
            }
    
            if(len < 0) {
                if(errno == EINTR) continue;
                break;
            }
            alllen += len;
            *rel = (unsigned char*)realloc(*rel,alllen+1);
            memcpy(*rel+alllen-len,tbuf,len);
            *(*rel+alllen) = 0;
        }
        
        if(fail) {
            strcpy(__adb_error, "monkey fault (no status)");
            return -1;
        }
    
        if(*rel != NULL && !memcmp(*rel, "OK", 2)) {
            return 0;
        }
    
        return -1;
    }
    

    OK,整个monkey功能的socket层就算完了

    应用层

    adb.exe中无法传送utf-8格式字符串的问题

    直接上代码,在执行命令的函数里面把buffer的编码变掉就可以

    int shell(char* inSerial, char* inParams, unsigned char** outRelStr)
    {
        int fd = 0;
        char buf[10240];
        unsigned char* rel = NULL;
        char* inParamsUTF8;
    
        transport_type ttype = kTransportAny;
        int server_port = DEFAULT_ADB_PORT;
    
        adb_set_transport(ttype, inSerial);
        adb_set_tcp_specifics(server_port);
    
        //这个函数就是关键的编码转换函数
        GBK_to_UTF8(inParams,strlen(inParams),&inParamsUTF8);
        ZeroMemory(buf,10240);
        snprintf(buf,sizeof buf,"shell:%s",inParamsUTF8);
        free(inParamsUTF8);
        fd = adb_connect(buf);  //+
        if(fd >= 0) {
            char buf[4096];
            int len;
            int alllen=0;
            int first = 0;
    
            while(fd >= 0) {
                len = adb_read(fd, buf, 4096);
                if(len == 0) {
                    break;
                }
    
                if(len < 0) {
                    if(errno == EINTR) continue;
                    break;
                }
                alllen+=len;
                rel = (unsigned char*)realloc(rel,alllen+1);
                memcpy(rel+alllen-len,buf,len);
                rel[alllen] = 0;
                //先做调试用,后期去掉
                //fwrite(buf, 1, len, stdout);
                //fflush(stdout);
            }
            * outRelStr = rel;
            adb_close(fd);
            return 1;
        }
        return 0;
    }
    

    GBK_to_UTF8函数的代码不贴出来了,网上非常多

    增加传送monkey指令的功能

    /*
    初始化monkey的连接,我们采用连接一次成功后,可以无限发送命令的模式
    连接deviceSocket并查询版本信息
    连接monkeySocket连接成功就行
    返回:是否成功 1成功 0失败
    */
    int init(char* inSerial, int inMonkeyPort, int* outMonkeySocket)
    {
        transport_type ttype = kTransportAny;
        int server_port = DEFAULT_ADB_PORT;
        char *tmp;
        unsigned char* mrel = NULL;
        char buf[4096];
        int fd = 0;
    
        adb_trace_init();
        adb_sysdeps_init();
    
        //这里只是设置一个模式,没有起实质性的变化
        adb_set_transport(ttype, inSerial);
        adb_set_tcp_specifics(server_port);
    
        //打开Monkey的端口
        snprintf(buf, sizeof buf, "host-serial:%s:forward:tcp:%d;tcp:%d",inSerial,inMonkeyPort,inMonkeyPort);
        fd = adb_connect(buf);
        if(fd >= 0) {
            if (adb_status(fd))
            {
                adb_close(fd);
                return 0;
            }
            read_finished(fd);
            adb_close(fd);
        } else {
            fprintf(stderr,"error: %s\n", adb_error());
            return 0;
        }
    
        //把手机端的Monkey端口设置为我们的端口
        //这是一个bug貌似,非常低端... monkey是阻塞命令,如果连接成功之后直接close这样monkey程序就会退出,所以睡5秒再说,官方的方法就是睡的5秒,如果有更好的办法就太好了
        set_monkey_port(inMonkeyPort);
        snprintf(buf, sizeof buf, "shell:monkey --port %d", inMonkeyPort);
        fd = adb_connect(buf);
        printf("monkey --port %d done\r\n",inMonkeyPort);
        if(fd >= 0)
        {
            Sleep(5000);
            adb_close(fd);
        }
        else
        {
            return 0;
        }
        printf("monkey --port %d ok\r\n",inMonkeyPort);
    
        //唤醒屏幕
        printf("wake\n",buf);
        *outMonkeySocket = _adb_monkey_connect( "wake\n",-1,&mrel);
        if (mrel!=NULL)
        {
            free(mrel);
        }
        if(*outMonkeySocket < 0) {
            fprintf(stderr,"error: %s\n", get_adb_error());
            return 0;
        }
    
        return 1;
    }
    

    下面例举几个monkey的应用层命令

    int wake(int inMonkeySocket)
    {
        return adb_monkey_exec("wake\n",inMonkeySocket);
    }
    
    int press(int inMonkeySocket, char* inKeyName, char* intPressType)
    {
        char buf[4096];
    
        if (strncmp("down",intPressType,sizeof("down"))==0)
        {
            snprintf(buf, sizeof buf,"key down %s\n",inKeyName);
        } 
        else if (strncmp("up",intPressType,sizeof("up"))==0)
        {
            snprintf(buf, sizeof buf,"key up %s\n",inKeyName);
        }
        else if (strncmp("downAndUp",intPressType,sizeof("downAndUp"))==0)
        {
            snprintf(buf, sizeof buf,"press %s\n",inKeyName);
        }
        
        return adb_monkey_exec(buf,inMonkeySocket);
    }
    

    OK,先分析思路,再搭建底层,最后建立应用层,整个adb.exe,或者adb.dll其他的,就可以操控整个android系统了,非常实用。下一讲讲啥呢?操控是可以,我要根据android设备的屏幕输出判断程序结果是否正确怎么办?怎么获取图像?怎么辨别?

    相关文章

      网友评论

        本文标题:让Adb.exe支持Monkey

        本文链接:https://www.haomeiwen.com/subject/rwywcttx.html