讓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閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,659評論 25 708
  • 移動APP測試講義 本篇講義主要闡述APP的手工測試要點,并概括介紹主流的APP測試框架。 1. APP測試的準備...
    厲鉚兄閱讀 9,697評論 6 109
  • 導語:人們對佛教常常有許多似是而非的看法,比如“學佛的人一定是在生活上遭受了打擊,學佛一定要出家,要學佛就必須要吃...
    行愿文化閱讀 669評論 0 0
  • (模仿練習) (第一次嘗試畫這種風格的,以后多試試,哈哈!)
    萌萌321閱讀 286評論 2 4
  • 今天翻看微博,看到了07年那幾個“老”男人昨天相聚,今天統一發了微博來秀恩愛,我的思緒,也被他們拉回了07年的那...
    Poisonous_姜小花閱讀 273評論 2 0