Android ADB 源码分析(三)
前言
之前分析的兩篇文章
Android Adb 源碼分析(一)
嵌入式Linux:Android root破解原理(二)
?
寫完之后,都沒有寫到相關(guān)的實(shí)現(xiàn)代碼,這篇文章寫下ADB的通信流程的一些細(xì)節(jié)
看這篇文章之前,請(qǐng)先閱讀
Linux的SOCKET編程詳解 - 江召偉 - 博客園
對(duì)socket通信有簡單的了解
1、ADB基本通信
理解:
(1)adb的本質(zhì),就是socket的通信,通過secket傳送數(shù)據(jù)及文件
(2)adb傳送是以每個(gè)固定格式的包發(fā)送的數(shù)據(jù),包的格式如下:
#define A_SYNC 0x434e5953 #define A_CNXN 0x4e584e43 #define A_OPEN 0x4e45504f #define A_OKAY 0x59414b4f #define A_CLSE 0x45534c43 #define A_WRTE 0x45545257 #define A_AUTH 0x48545541struct amessage {unsigned command; /* command identifier constant */unsigned arg0; /* first argument */unsigned arg1; /* second argument */unsigned data_length; /* length of payload (0 is allowed) */unsigned data_check; /* checksum of data payload */unsigned magic; /* command ^ 0xffffffff */ };struct apacket {apacket *next;unsigned len;unsigned char *ptr;amessage msg;unsigned char data[MAX_PAYLOAD]; };發(fā)送的包格式為apacket格式,其中msg為消息部分,data為數(shù)據(jù)部分。msg的消息類型有很多種,包括A_SYNC,?A_CNXN,?A_OPEN,?A_OKAY等等。
(3)adb給我們預(yù)留了調(diào)試的信息,我們只需要在adb.h中定義指定的宏,即可看到每次數(shù)據(jù)的傳輸過程:
#define DEBUG_PACKETS 1
打開調(diào)試信息后,我們可以看到傳輸過程中的細(xì)節(jié),在串口打印里面。
(4)我們使用adb push命令,來跟蹤分析下這個(gè)apacket數(shù)據(jù)是怎樣傳輸?shù)?#xff1a;
我們以adb push profile /命令為例,在串口我們可以看見如下詳細(xì)的傳輸信息:
status command arg0 arg1 len data
recv: OPEN 00141028 00000000 0006 "sync:."
send: OKAY 0000003e 00141028 0000 ""
recv: WRTE 00141028 0000003e 0009 "STAT..../"
send: OKAY 0000003e 00141028 0000 ""
send: WRTE 0000003e 00141028 0010 "STAT.A......[oHZ"
recv: OKAY 00141028 0000003e 0000 ""
recv: WRTE 00141028 0000003e 0027 "SEND..../profile,33206DATA....2D
send: OKAY 0000003e 00141028 0000 ""
send: WRTE 0000003e 00141028 0008 "OKAY...."
recv: OKAY 00141028 0000003e 0000 ""
recv: WRTE 00141028 0000003e 0008 "QUIT...."
send: OKAY 0000003e 00141028 0000 ""
send: CLSE 00000000 00141028 0000 ""
recv: CLSE 00141028 0000003e 0000 ""
以上recv表示接收的數(shù)據(jù)包,send表示回傳的數(shù)據(jù)包。后面五個(gè)分別為數(shù)據(jù)包的數(shù)據(jù)字段值(command arg0 arg1 len data),這樣數(shù)據(jù)我們還是不夠直觀,我們翻譯成更加直接的數(shù)據(jù)輔以文字解釋
這樣是不是容易理解多了呢,經(jīng)過這樣的數(shù)據(jù)發(fā)送,我們就通過adb push命令把本地的profile文件推送到遠(yuǎn)程設(shè)備的根目錄了。哇..... 原來這么簡單,一個(gè)profile文件就傳輸了。流程理解了,我們?cè)賮砜创a,現(xiàn)在結(jié)果你知道了,流程你也懂了,再來看源碼,是不是容易理解了呢。
?
2、代碼分析
我們看代碼也是逆向的看,這樣利于我們理解,不會(huì)被源碼看到暈乎乎,上面流程懂了,知道了每次是以apacket的格式發(fā)送的,我們先來研究這個(gè)apacket的接收與發(fā)送函數(shù)。
接收函數(shù)handle_packet
void handle_packet(apacket *p, atransport *t) {asocket *s;D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],((char*) (&(p->msg.command)))[1],((char*) (&(p->msg.command)))[2],((char*) (&(p->msg.command)))[3]);print_packet("recv", p);switch(p->msg.command){case A_SYNC:if(p->msg.arg0){send_packet(p, t);if(HOST) send_connect(t);} else {t->connection_state = CS_OFFLINE;handle_offline(t);send_packet(p, t);}return;case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") *//* XXX verify version, etc */if(t->connection_state != CS_OFFLINE) {t->connection_state = CS_OFFLINE;handle_offline(t);}parse_banner((char*) p->data, t);if (HOST || !auth_enabled) {handle_online(t);if(!HOST) send_connect(t);} else {send_auth_request(t);}break;case A_AUTH:if (p->msg.arg0 == ADB_AUTH_TOKEN) {t->key = adb_auth_nextkey(t->key);if (t->key) {send_auth_response(p->data, p->msg.data_length, t);} else {/* No more private keys to try, send the public key */send_auth_publickey(t);}} else if (p->msg.arg0 == ADB_AUTH_SIGNATURE) {if (adb_auth_verify(t->token, p->data, p->msg.data_length)) {adb_auth_verified(t);t->failed_auth_attempts = 0;} else {if (t->failed_auth_attempts++ > 10)adb_sleep_ms(1000);send_auth_request(t);}} else if (p->msg.arg0 == ADB_AUTH_RSAPUBLICKEY) {adb_auth_confirm_key(p->data, p->msg.data_length, t);}break;case A_OPEN: /* OPEN(local-id, 0, "destination") */if (t->online) {char *name = (char*) p->data;name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;s = create_local_service_socket(name);if(s == 0) {send_close(0, p->msg.arg0, t);} else {s->peer = create_remote_socket(p->msg.arg0, t);s->peer->peer = s;send_ready(s->id, s->peer->id, t);s->ready(s);}}break;case A_OKAY: /* READY(local-id, remote-id, "") */if (t->online) {if((s = find_local_socket(p->msg.arg1))) {if(s->peer == 0) {s->peer = create_remote_socket(p->msg.arg0, t);s->peer->peer = s;}s->ready(s);}}break;case A_CLSE: /* CLOSE(local-id, remote-id, "") */if (t->online) {if((s = find_local_socket(p->msg.arg1))) {s->close(s);}}break;case A_WRTE:if (t->online) {if((s = find_local_socket(p->msg.arg1))) {unsigned rid = p->msg.arg0;p->len = p->msg.data_length;if(s->enqueue(s, p) == 0) {D("Enqueue the socket\n");send_ready(s->id, rid, t);}return;}}break;default:printf("handle_packet: what is %08x?!\n", p->msg.command);}put_apacket(p); }哇,這個(gè)函數(shù)好像不復(fù)雜
一個(gè)函數(shù),然后解析apacket *p數(shù)據(jù),根據(jù)msg.command的命令值, 然后對(duì)應(yīng)不同的case,有著不同的響應(yīng)。事實(shí)上也就是這樣,這個(gè)函數(shù)主要就是根據(jù)不同的消息類型,來處理這個(gè)apacket的數(shù)據(jù)。
下面解析下上面push命令的過程
1、OPEN響應(yīng)
recv: OPEN 00141028 00000000 0006 "sync:."
send: OKAY 0000003e 00141028 0000 ""
接收到了OPEN的消息,然后附帶了一個(gè)sync的數(shù)據(jù),我們看看是如何響應(yīng)的。
case A_OPEN: /* OPEN(local-id, 0, "destination") */if (t->online) {char *name = (char*) p->data;name[p->msg.data_length > 0 ? p->msg.data_length - 1 : 0] = 0;s = create_local_service_socket(name);if(s == 0) {send_close(0, p->msg.arg0, t);} else {s->peer = create_remote_socket(p->msg.arg0, t);s->peer->peer = s;send_ready(s->id, s->peer->id, t);s->ready(s);}}break;調(diào)用create_local_service_socket(“sync”);
fd = service_to_fd(name);
//創(chuàng)建本地socket,并為這個(gè)socket創(chuàng)建數(shù)據(jù)處理線程file_sync_service
ret = create_service_thread(file_sync_service, NULL);
//把這個(gè)本地socket關(guān)聯(lián)到結(jié)構(gòu)asocket *s
s = create_local_socket(fd);
調(diào)用create_remote_socket(p->msg.arg0, t); //把遠(yuǎn)程的socket也與這個(gè)結(jié)構(gòu)體asocket 關(guān)聯(lián)。
如上兩個(gè)函數(shù)調(diào)用,主要是初始化本地的socket對(duì),本地socket用來跟后臺(tái)服務(wù)線程之間的通信,以及跟對(duì)應(yīng)命令的后臺(tái)服務(wù)線程通信。初始化adb通信的環(huán)境。其中asocket *s為本地socket與遠(yuǎn)程socket的一個(gè)關(guān)聯(lián)結(jié)構(gòu)體,其中s保存的是本地socket的信息,s->peer保存的是遠(yuǎn)程socket相關(guān)的信息。
send_ready(s->id, s->peer->id, t); 然后發(fā)送OKAY給PC端。
static void send_ready(unsigned local, unsigned remote, atransport *t) {D("Calling send_ready \n");apacket *p = get_apacket();p->msg.command = A_OKAY;p->msg.arg0 = local;p->msg.arg1 = remote;send_packet(p, t); }這個(gè)與我們看到的流程相符合。接收到OPEN的消息,初始化一些狀態(tài),然后返回一個(gè)OKAY的狀
2、WRITE響應(yīng)
recv: WRTE 00141028 0000003e 0009 "STAT..../"
send: OKAY 0000003e 00141028 0000 ""
send: WRTE 0000003e 00141028 0010 "STAT.A......[oHZ"
接收到了WRITE的消息,順帶了一個(gè)查詢STAT的數(shù)據(jù),我們看看是如何響應(yīng)的:
case A_WRTE:if (t->online) {if((s = find_local_socket(p->msg.arg1))) {unsigned rid = p->msg.arg0;p->len = p->msg.data_length;if(s->enqueue(s, p) == 0) {D("Enqueue the socket\n");send_ready(s->id, rid, t);}return;}}break;先通過參數(shù)p->msg.arg1找到我們?cè)贠PEN的時(shí)候建立的結(jié)構(gòu)體信息asocket *s, 然后處理本地socket隊(duì)列中的數(shù)據(jù)(s為本地,s->peer為遠(yuǎn)程)
s->enqueue(s, p)即為之前 關(guān)聯(lián)的函數(shù)local_socket_enqueue,其在create_local_socket(fd); 的時(shí)候設(shè)置。
static int local_socket_enqueue(asocket *s, apacket *p) {D("LS(%d): enqueue %d\n", s->id, p->len);p->ptr = p->data;/* if there is already data queue'd, we will receive** events when it's time to write. just add this to** the tail*/if(s->pkt_first) {goto enqueue;}/* write as much as we can, until we** would block or there is an error/eof*/while(p->len > 0) {int r = adb_write(s->fd, p->ptr, p->len);if(r > 0) {p->len -= r;p->ptr += r;continue;}if((r == 0) || (errno != EAGAIN)) {D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );s->close(s);return 1; /* not ready (error) */} else {break;}}if(p->len == 0) {put_apacket(p);return 0; /* ready for more data */}enqueue:p->next = 0;if(s->pkt_first) {s->pkt_last->next = p;} else {s->pkt_first = p;}s->pkt_last = p;/* make sure we are notified when we can drain the queue */fdevent_add(&s->fde, FDE_WRITE);return 1; /* not ready (backlog) */ }我們通過adb_write(s->fd, p->ptr, p->len)把要處理的數(shù)據(jù),寫入到本地socket對(duì)應(yīng)的fd中,等待處理。
然后調(diào)用send_ready(s->id, rid, t);返回一個(gè)OKAY的狀態(tài)
我們把待處理的數(shù)據(jù)adb_write之后,又是在哪里處理的呢,我們之前在創(chuàng)建本地socket的時(shí)候,就創(chuàng)建了一個(gè)線程,對(duì)應(yīng)的處理socket數(shù)據(jù)的函數(shù)file_sync_service。
我們來看看file_sync_service函數(shù)是如何處理的
void file_sync_service(int fd, void *cookie) {syncmsg msg;char name[1025];unsigned namelen;char *buffer = malloc(SYNC_DATA_MAX);if(buffer == 0) goto fail;for(;;) {D("sync: waiting for command\n");if(readx(fd, &msg.req, sizeof(msg.req))) {fail_message(fd, "command read failure");break;}namelen = ltohl(msg.req.namelen);if(namelen > 1024) {fail_message(fd, "invalid namelen");break;}if(readx(fd, name, namelen)) {fail_message(fd, "filename read failure");break;}name[namelen] = 0;msg.req.namelen = 0;D("sync: '%s' '%s'\n", (char*) &msg.req, name);switch(msg.req.id) {case ID_STAT:if(do_stat(fd, name)) goto fail;break;case ID_LIST:if(do_list(fd, name)) goto fail;break;case ID_SEND:if(do_send(fd, name, buffer)) goto fail;break;case ID_RECV:if(do_recv(fd, name, buffer)) goto fail;break;case ID_QUIT:goto fail;default:fail_message(fd, "unknown command");goto fail;}}fail:if(buffer != 0) free(buffer);D("sync: done\n");adb_close(fd); }原來在這里處理的數(shù)據(jù),終于找到你, 我們收到的消息是查看路徑是否存在,這里對(duì)應(yīng)的就是ID_STAT,還有其他的消息處理,比如ID_SEND,ID_RECV,ID_QUIT,望文生義,我們就不具體解釋了。我們還是看看ID_STAT對(duì)應(yīng)的處理吧do_stat(fd, name)。
static int do_stat(int s, const char *path) {syncmsg msg;struct stat st;msg.stat.id = ID_STAT;if(lstat(path, &st)) {msg.stat.mode = 0;msg.stat.size = 0;msg.stat.time = 0;} else {msg.stat.mode = htoll(st.st_mode);msg.stat.size = htoll(st.st_size);msg.stat.time = htoll(st.st_mtime);}return writex(s, &msg.stat, sizeof(msg.stat)); }這里就是判斷路徑是否存在的邏輯了,這個(gè)就是我們想要的,我們把判斷的結(jié)果存儲(chǔ)在msg.stat, 然后把對(duì)應(yīng)的結(jié)果寫回去writex。
我們把檢測(cè)的狀態(tài)writex之后,但是這個(gè)數(shù)據(jù)還沒有發(fā)送回PC端啊,是在哪里發(fā)送回去的呢,我們繼續(xù)跟蹤
我們?cè)赾reate_local_socket創(chuàng)建本地socket的時(shí)候,順便還注冊(cè)了一個(gè)回調(diào)函數(shù)local_socket_event_func
static void local_socket_event_func(int fd, unsigned ev, void *_s) {asocket *s = _s;D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);/* put the FDE_WRITE processing before the FDE_READ** in order to simplify the code.*/if(ev & FDE_WRITE){apacket *p;while((p = s->pkt_first) != 0) {while(p->len > 0) {int r = adb_write(fd, p->ptr, p->len);if(r > 0) {p->ptr += r;p->len -= r;continue;}if(r < 0) {/* returning here is ok because FDE_READ will** be processed in the next iteration loop*/if(errno == EAGAIN) return;if(errno == EINTR) continue;}D(" closing after write because r=%d and errno is %d\n", r, errno);s->close(s);return;}if(p->len == 0) {s->pkt_first = p->next;if(s->pkt_first == 0) s->pkt_last = 0;put_apacket(p);}}/* if we sent the last packet of a closing socket,** we can now destroy it.*/if (s->closing) {D(" closing because 'closing' is set after write\n");s->close(s);return;}/* no more packets queued, so we can ignore** writable events again and tell our peer** to resume writing*/fdevent_del(&s->fde, FDE_WRITE);s->peer->ready(s->peer);}if(ev & FDE_READ){apacket *p = get_apacket();unsigned char *x = p->data;size_t avail = MAX_PAYLOAD;int r;int is_eof = 0;while(avail > 0) {r = adb_read(fd, x, avail);D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%d\n", s->id, s->fd, r, r<0?errno:0, avail);if(r > 0) {avail -= r;x += r;continue;}if(r < 0) {if(errno == EAGAIN) break;if(errno == EINTR) continue;}/* r = 0 or unhandled error */is_eof = 1;break;}D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",s->id, s->fd, r, is_eof, s->fde.force_eof);if((avail == MAX_PAYLOAD) || (s->peer == 0)) {put_apacket(p);} else {p->len = MAX_PAYLOAD - avail;r = s->peer->enqueue(s->peer, p);D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd, r);if(r < 0) {/* error return means they closed us as a side-effect** and we must return immediately.**** note that if we still have buffered packets, the** socket will be placed on the closing socket list.** this handler function will be called again** to process FDE_WRITE events.*/return;}if(r > 0) {/* if the remote cannot accept further events,** we disable notification of READs. They'll** be enabled again when we get a call to ready()*/fdevent_del(&s->fde, FDE_READ);}}/* Don't allow a forced eof if data is still there */if((s->fde.force_eof && !r) || is_eof) {D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n", is_eof, r, s->fde.force_eof);s->close(s);}}if(ev & FDE_ERROR){/* this should be caught be the next read or write** catching it here means we may skip the last few** bytes of readable data.*/ // s->close(s);D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);return;} }我們看后面if(ev & FDE_READ)部分:
adb_read(fd, x, avail);把數(shù)據(jù)讀出來,然后調(diào)用r = s->peer->enqueue(s->peer, p);,即把數(shù)據(jù)發(fā)送給遠(yuǎn)程socket的隊(duì)列處理。(s->speer即遠(yuǎn)程端,之前已經(jīng)說明)
s->peer->enqueue函數(shù)即remote_socket_enqueue
static int remote_socket_enqueue(asocket *s, apacket *p) {D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",s->id, s->fd, s->peer->fd);p->msg.command = A_WRTE;p->msg.arg0 = s->peer->id;p->msg.arg1 = s->id;p->msg.data_length = p->len;send_packet(p, s->transport);return 1; }這樣我們就把STAT的結(jié)果,通過WRITE返回給了PC端
這個(gè)與我們看到的流程也是相符的,接收到WRITE(STAT)的消息,先返回一個(gè)OKAY的狀態(tài),在返回WRITE(STAT)的結(jié)果。
我們可以觀察之前的數(shù)據(jù)接收及發(fā)送流程,可以發(fā)現(xiàn)每次一個(gè)WRITE消息,后面都是返回一個(gè)OKAY WRITE消息。
貼了這么多的代碼,是不是有點(diǎn)暈了,再貼就真的看不下去了,我們下面重新來理一理思路。
1. adb其實(shí)就是個(gè)socket通信,數(shù)據(jù)發(fā)過來發(fā)過去。
2. adb每次都是發(fā)送的一個(gè)數(shù)據(jù)包,數(shù)據(jù)結(jié)構(gòu)是struct apacket,其中包含msg消息部分,及data數(shù)據(jù)部分。
3. 從PC跟device通信的過程,有一條協(xié)議流程,通過不斷的數(shù)據(jù)交互發(fā)送,實(shí)現(xiàn)數(shù)據(jù)文件傳遞。
4. 我們可以定義 #define DEBUG_PACKETS 1 這樣可以看到socket通信的數(shù)據(jù)發(fā)送過程。
5. socket數(shù)據(jù)建立傳輸過程,會(huì)創(chuàng)建socket,創(chuàng)建事件監(jiān)聽線程,注冊(cè)回調(diào)響應(yīng)函數(shù),亂七八糟的....
?
如果覺得不錯(cuò),請(qǐng)關(guān)注公眾號(hào)【嵌入式Linux】,謝謝
總結(jié)
以上是生活随笔為你收集整理的Android ADB 源码分析(三)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 贝壳房价数据分析
- 下一篇: FPGA、AD9371、AD9009、R