让Adb.exe支持Monkey

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

下载源码

要改造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设备的屏幕输出判断程序结果是否正确怎么办?怎么获取图像?怎么辨别?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,856评论 25 707
  • 移动APP测试讲义 本篇讲义主要阐述APP的手工测试要点,并概括介绍主流的APP测试框架。 1. APP测试的准备...
    厲铆兄阅读 9,614评论 6 109
  • 导语:人们对佛教常常有许多似是而非的看法,比如“学佛的人一定是在生活上遭受了打击,学佛一定要出家,要学佛就必须要吃...
    行愿文化阅读 645评论 0 0
  • (模仿练习) (第一次尝试画这种风格的,以后多试试,哈哈!)
    萌萌321阅读 282评论 2 4
  • 今天翻看微博,看到了07年那几个“老”男人昨天相聚,今天统一发了微博来秀恩爱,我的思绪,也被他们拉回了07年的那...
    Poisonous_姜小花阅读 260评论 2 0