天天看點

android編譯adb.exe,讓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所在的源碼目錄

到了這裡,是不是要去找一些官方文檔,該目錄下的一些文檔說明來進行編譯,太糾結了,去github上找找吧,現在是共享時代

啰嗦了一堆,其實就是下載下傳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 [:]\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互動的,還記得上一篇文章的圖嗎?

android編譯adb.exe,讓Adb.exe支援Monkey

Adb功能結構圖

執行monkey指令流程流程是這樣的,一次連接配接可能會執行很多次指令

android編譯adb.exe,讓Adb.exe支援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指令的功能

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裝置的螢幕輸出判斷程式結果是否正确怎麼辦?怎麼擷取圖像?怎麼辨識?