由於要用到adb的知識,但是對adb啥也不了解,看了下android的代碼,adb的源碼在system/core/adb下面,然后網上搜下了資料,發現很多大神的源碼分析,瞬間信心爆棚,把大神寫的博客都瀏覽了一遍,然后手動運行了下adb命令,順便跟蹤了下過程,發現原來還是很好的理解,源碼的各種線程創建,函數回調,對於我這種基礎不咋好的,,還是看的暈暈呼呼,現在把我自己的理解給大家分享,有理解錯誤的還請多多指正。
一般直接上代碼,可能看官都走了一大半,我們逆向的看,先看結果,再看過程,最后再看代碼。從簡單的入手。
理解:
(1)adb的本質,就是socket的通信,通過secket傳送數據及文件
(2)adb傳送是以每個固定格式的包發送的數據,包的格式如下:
#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 0x48545541 struct 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]; };
發送的包格式為apacket格式,其中msg為消息部分,data為數據部分。msg的消息類型有很多種,包括A_SYNC, A_CNXN, A_OPEN, A_OKAY等等。
到此,我們對adb的理解就是,一種socket通信,每次發送apacket格式的數據包,好吧,我們在這樣的認知下繼續學習adb。
(3)adb給我們預留了調試的信息,我們只需要在adb.h中定義指定的宏,即可看到每次數據的傳輸過程:
#define DEBUG_PACKETS 1
(4)我們使用adb push命令,來跟蹤分析下這個apacket數據是怎樣傳輸的:
我們以adb push profile /命令為例,在串口我們可以看見如下詳細的傳輸信息:
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表示接收的數據包,send表示回傳的數據包。后面五個分別為數據包的數據字段值(command arg0 arg1 len data),這樣數據我們還是不夠直觀,我們翻譯成更加直接的數據輔以文字解釋
這樣是不是容易理解多了呢,經過這樣的數據發送,我們就通過adb push命令把本地的profile文件推送到遠程設備的根目錄了。哇..... 原來這么簡單,一個profile文件就傳輸了。流程理解了,我們再來看代碼,現在結果你知道了,流程你也懂了,再來看源碼,是不是容易理解了呢。
(5)同樣,我們看代碼也是逆向的看,這樣利於我們理解,不會被源碼看到暈乎乎,上面流程懂了,知道了每次是以apacket的格式發送的,我們先來研究這個apacket的接收與發送函數。
接收函數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); }
哇,這個函數好像不復雜,一個函數,然后解析apacket *p數據,根據msg.command的命令值, 然后對應不同的case,有着不同的響應。事實上也就是這樣,這個函數主要就是根據不同的消息類型,來處理這個apacket的數據。
上面不是有adb push命令嗎,我們根據這個流程,看看handle_packet是否是跟我們預期的響應流程一樣。
(5.1)OPEN響應
recv: OPEN 00141028 00000000 0006 "sync:."
send: OKAY 0000003e 00141028 0000 ""
接收到了OPEN的消息,然后附帶了一個sync的數據,我們看看是如何響應的。
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;
調用create_local_service_socket(“sync”);
fd = service_to_fd(name);
//創建本地socket,並為這個socket創建數據處理線程file_sync_service
ret = create_service_thread(file_sync_service, NULL);
//把這個本地socket關聯到結構asocket *s
s = create_local_socket(fd);
調用create_remote_socket(p->msg.arg0, t); //把遠程的socket也與這個結構體asocket 關聯。
如上兩個函數調用,主要是初始化本地的socket對,本地socket用來跟后台服務線程之間的通信,以及跟對應命令的后台服務線程通信。初始化adb通信的環境。其中asocket *s為本地socket與遠程socket的一個關聯結構體,其中s保存的是本地socket的信息,s->peer保存的是遠程socket相關的信息。
send_ready(s->id, s->peer->id, t); 然后發送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); }
這個與我們看到的流程相符合。接收到OPEN的消息,初始化一些狀態,然后返回一個OKAY的狀態
(5.2)WRITE響應
recv: WRTE 00141028 0000003e 0009 "STAT..../"
send: OKAY 0000003e 00141028 0000 ""
send: WRTE 0000003e 00141028 0010 "STAT.A......[oHZ"
接收到了WRITE的消息,順帶了一個查詢STAT的數據,我們看看是如何響應的:
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;
先通過參數p->msg.arg1找到我們在OPEN的時候建立的結構體信息asocket *s, 然后處理本地socket隊列中的數據(s為本地,s->peer為遠程)
s->enqueue(s, p)即為之前 關聯的函數local_socket_enqueue,其在create_local_socket(fd); 的時候設置。
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)把要處理的數據,寫入到本地socket對應的fd中,等待處理。
然后調用send_ready(s->id, rid, t);返回一個OKAY的狀態
我們把待處理的數據adb_write之后,又是在哪里處理的呢,我們之前在創建本地socket的時候,就創建了一個線程,對應的處理socket數據的函數file_sync_service。
我們來看看file_sync_service函數是如何處理的
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); }
原來在這里處理的數據,終於找到你, 我們收到的消息是查看路徑是否存在,這里對應的就是ID_STAT,還有其他的消息處理,比如ID_SEND,ID_RECV,ID_QUIT,望文生義,我們就不具體解釋了。我們還是看看ID_STAT對應的處理吧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)); }
這里就是判斷路徑是否存在的邏輯了,這個就是我們想要的,我們把判斷的結果存儲在msg.stat, 然后把對應的結果寫回去writex。
我們把檢測的狀態writex之后,但是這個數據還沒有發送回PC端啊,是在哪里發送回去的呢,我們繼續跟蹤 我們在create_local_socket創建本地socket的時候,順便還注冊了一個回調函數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);把數據讀出來,然后調用r = s->peer->enqueue(s->peer, p);,即把數據發送給遠程socket的隊列處理。(s->speer即遠程端,之前已經說明)
s->peer->enqueue函數即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的結果,通過WRITE返回給了PC端
這個與我們看到的流程也是相符的,接收到WRITE(STAT)的消息,先返回一個OKAY的狀態,在返回WRITE(STAT)的結果。
我們可以觀察之前的數據接收及發送流程,可以發現每次一個WRITE消息,后面都是返回一個OKAY WRITE消息。
貼了這么多的代碼,是不是有點暈了,再貼就真的看不下去了,我們下面重新來理一理思路。
1. adb其實就是個socket通信,數據發過來發過去。
2. adb每次都是發送的一個數據包,數據結構是struct apacket,其中包含msg消息部分,及data數據部分。
3. 從PC跟device通信的過程,有一條協議流程,通過不斷的數據交互發送,實現數據文件傳遞。
4. 我們可以定義 #define DEBUG_PACKETS 1 這樣可以看到socket通信的數據發送過程。
5. socket數據建立傳輸過程,會創建socket,創建事件監聽線程,注冊回調響應函數,亂七八糟的......
6. 然后就是一系列的代碼流程了,頭暈了,我們下節再來詳細理一理這個,為啥要按照你說的這個流程走,為啥你說走到這里,調用這個函數,憑啥相信你,下節見......