大部分編程人員進行程序代碼分析時往往喜歡從main函數入手,因為它是程序入口,從main開始能夠更快更好的把握程序的整體結構。
因此我們先來看adb.c的main函數的實現:
C++代碼
- int main(int argc, char **argv)
- {
- adb_trace_init();
- #if ADB_HOST
- adb_sysdeps_init();
- 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);
- #endif
- }
宏ADB_HOST用來區別編譯adb和adbd,參見上一篇文章:Android開發工具ADB教程之一:ADB概論。
現在用一個常用命令“adb devices”用來捋順代碼流程,adb_trace_init用於log tag初始化,在host端,輸入命令"adb devices"之後,進入adb_commandline函數。
adb_commandline首先解析參數,判斷有沒有指定transport type,即指定與哪個設備通信,emulator或者device,指定設備的方法是
-d
-e
-s <serial number>
然後調用adb_set_transport將type,serial賦值給全局變量。
C++代碼
- void adb_set_transport(transport_type type, const char* serial)
- {
- __adb_transport = type;
- __adb_serial = serial;
- }
這兩個全局變量由client保存,將用來告訴server,與何種設備通信,用何種方式傳輸通信。
接下來,adb_commandline用來判斷server守護進程是否已經啟動。
C++代碼
- if ((argc > 0) && (!strcmp(argv[0],"server"))) {
- if (no_daemon || is_daemon) {
- r = adb_main(is_daemon);
- } else {
- r = launch_server();
- }
- if(r) {
- fprintf(stderr,"* could not start server *\n");
- }
- return r;
- }
no_daemon和is_daemon初始化為0,當讀到nodaemon參數時,no_daemon為1,這種情況用戶顯式的不用server進行通信;讀到fork-server時is_daemon為1,這是標識當前進程已經是server進程。adb_main函數的is_daemon參數是用來決定是否回送一個應答“OK”給client的。
在這裡我們的client第一次執行“adb device”,因此會去啟動server,在launch_server中,執行fork()操作,生成一對管道用於父子進程的通信。子進程調用execl,執行adb fork-server server,父進程等待來自子進程的OK應答。
C++代碼
- // child side of the fork
-
- // redirect stderr to the pipe
- // we use stderr instead of stdout due to stdout's buffering behavior.
- adb_close(fd[0]);
- dup2(fd[1], STDERR_FILENO);
- adb_close(fd[1]);
-
- // child process
- int result = execl(path, "adb", "fork-server", "server", NULL);
- // this should not return
- fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
這裡子進程將STDERR_FILENO重定向到管道的寫端fd[1];然後講管道關閉,這樣所有對stderr的操作都將寫入父進程,fprintf語句只有在execl執行失敗時執行。
C++代碼
- // parent side of the fork
-
- char temp[3];
-
- temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
- // wait for the "OK\n" message
- adb_close(fd[1]);
- int ret = adb_read(fd[0], temp, 3);
- adb_close(fd[0]);
- if (ret < 0) {
- fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);
- return -1;
- }
- if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {
- fprintf(stderr, "ADB server didn't ACK\n" );
- return -1;
- }
-
- setsid();
父進程從管道的讀端讀取子進程發過來的應答,如果非“OK”,代表創建server失敗,返回,setsid用於避免父進程退出時子進程也退出,即server真正成為一個守護進程。
花開兩朵各表一只,先看fork出來的子進程,即守護進程server,前面說到,它的啟動command是adb fork-server server,我們在回到adb的main函數,它用走到了adb_commandline裡面來,這時候它解析參數以後,is_daemon就變成1了,因此執行adb_daemon(is_daemon)。
C++代碼
- init_transport_registration();
-
-
- ADB_HOST
- HOST = 1;
- usb_vendors_init();
- usb_init();
- local_init(ADB_LOCAL_TRANSPORT_PORT);
-
- if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
- exit(1);
- }
- e
adb_daemon首先初始化transport registrantion,等待注冊transport時間的到來,transport是用來與遠端設備進行通信的,對HOST來說遠端設備就是device/emulator,反之亦然。注冊信息本身,是用通過一個socket對transport_registration_send,transport_registration_recv來傳遞的,這屬於線程之間的通信。
首先初始化本地USB,監聽本地usb的情況,如果有用於ADB的USB設備,則注冊一個type為kTransportUsb的transport。
具體調用流程:usb_init->client_socket_thread出一個device_poll_thread線程,在device_poll_thread中:
C++代碼
- for (;;) {
- sleep(5);
- kick_disconnected();
- scan_usb_devices();
- }
通過scan_usb_devices查找用於adb的usb設備:
scan_usb_devices->check_device->register_device->register_usb_transport->init_usb_transport->register_transport。
在init_usb_transport中:
C++代碼
- void init_usb_transport(atransport *t, usb_handle *h, int state)
- {
- D("transport: usb\n");
- t->close = remote_close;
- t->kick = remote_kick;
- t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
- t->sync_token = 1;
- t->connection_state = state;
- t->type = kTransportUsb;
- t->usb = h;
-
- #if ADB_HOST
- HOST = 1;
- #else
- HOST = 0;
- #endif
- }
可以看到,不管在host端,還是在device端,都會去注冊usb的transport。
接著然後試圖連接5555-55585之間的端口,這個時候如果已經有emulator在運行,即調用socket_network_client成功,則注冊一個type為kTransportLocal的transport,調用流程:local_init->adb_thread_create出一個client_socket_thread線程,在client_socket_thread中,嘗試連接5555-55585的本地端口:client_socket_thread->socket_loopback_client。
如果socket_loopback_client返回值大於0,說明已連接上emulator,則調用:register_socket_transport->init_socket_transport->register_transport。
在init_socket_transport中:
C++代碼
- int init_socket_transport(atransport *t, int s, int port, int local)
- {
- int fail = 0;
-
- t->kick = remote_kick;
- t->close = remote_close;
- t->read_from_remote = remote_read;
- t->write_to_remote = remote_write;
- t->sfd = s;
- t->sync_token = 1;
- t->connection_state = CS_OFFLINE;
- t->type = kTransportLocal;
-
- #if ADB_HOST
- if (HOST && local) {
- adb_mutex_lock( &local_transports_lock );
- {
- int index = (port - ADB_LOCAL_TRANSPORT_PORT)/2;
-
- if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) {
- D("bad local transport port number: %d\n", port);
- fail = -1;
- }
- else if (local_transports[index] != NULL) {
- D("local transport for port %d already registered (%p)?\n",
- port, local_transports[index]);
- fail = -1;
- }
- else
- local_transports[index] = t;
- }
- adb_mutex_unlock( &local_transports_lock );
- }
- #endif
- return fail;
- }
注意看ADB_HOST裡面的東西,如果是在HOST端,則將transport添加到列表裡面,因為adb device就是從這個列表裡面讀信息的。
再看register_transport,它將transport信息,一個tmsp的結構體,寫入transport_registration_send。
C++代碼
- struct tmsg
- {
- atransport *transport;
- int action;
- };
action為0表示移除該transport,1表示添加。
則接收端的描述符transport_registration_recv會收到對應的信息,它的處理回調函數是transport_registration_func,在transport_registration_func中,首先讀取出待注冊的transport的地址,在這裡創建套接字對,一個是fd,負責從遠端讀入,或者寫入遠端。transport_socket負責跟本地(emulator或者device)交互,同時啟動兩個線程output_thread,調用read_from_remote從遠端讀入,還有input_thread,調用write_to_remote寫入遠端。
以output_thread為例:
C++代碼
- p = get_apacket();
- p->msg.command = A_SYNC;
- p->msg.arg0 = 1;
- p->msg.arg1 = ++(t->sync_token);
- p->msg.magic = A_SYNC ^ 0xffffffff;
- if(write_packet(t->fd, &p)) {
- put_apacket(p);
- D("from_remote: failed to write SYNC apacket to transport %p", t);
- goto oops;
- }
首先向fd寫入一個包含A_SYNC命令的信息包,用於同步,transport_socket的處理回調函數transport_socket_events會執行,繼而調用handle_packet處理信息包。
C++代碼
- 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;
handle_packet判斷是A_SYNC同步命令,則同時將信息包發送給遠端,並發送一個連接請求給遠端,裡面包含adb版本,最大載荷等信息。send_package講信息包寫回給transport_socket。
回過頭來output_thread線程回收到這些信息包,並將這些包寫入遠端。
寫的太深入了,回到adb_main函數,初始化完可能USB和emulator的transport之後,執行下面這段代碼:
C++代碼
- if(install_listener("tcp:5037", "*smartsocket*", NULL)) {
- exit(1);
- }
listener是一個很重要的概念,它綁定到一個本地端口,即local socket,負責與client通信(稍候將看到),並且創建一個連接到遠端的remote socket,smartsocket是一個特殊的socket,它其實類似一個接線員的角色,它分析後看你要連接到哪個remote socket,然後幫你連上。注意這裡的第三個參數NULL,因為接線員還來不及分析你的adb命令參數,不知道你要往哪個remote上連,所以這裡為NULL,等分析好了,確定要連接那個remote socket,smartsocket的任務也完成了。
C++代碼
- struct alistener
- {
- alistener *next;
- alistener *prev;
-
- fdevent fde;
- int fd;
-
- const char *local_name;
- const char *connect_to;
- atransport *transport;
- adisconnect disconnect;
- };
在install_listener裡面:
C++代碼
- l->fd = local_name_to_fd(local_name);
- close_on_exec(l->fd);
- if(!strcmp(l->connect_to, "*smartsocket*")) {
- fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);
- }
這樣,client來消息的時候,就可以調用ss_listener_event_func進行處理了。
接下來,adb_main執行下面代碼:
C++代碼
- if (is_daemon)
- {
- // inform our parent that we are up and running.
- f defined(HAVE_FORKEXEC)
- fprintf(stderr, "OK\n");
- if
- start_logging();
- }
這段代碼,告訴父進程adb server已經跑起來了,因此,往stderr裡面寫一個OK,還記得剛剛server已經將stderr重定向到fd[1]了,所以父進程能接收到這個OK消息。
接下來,server調用fdevent_loop進入事件循環。
C++代碼
- for(;;) {
- fdevent_process();
-
- while((fde = fdevent_plist_dequeue())) {
- unsigned events = fde->events;
- fde->events = 0;
- fde->state &= (~FDE_PENDING);
- dump_fde(fde, "callback");
- fde->func(fde->fd, events, fde->arg);
- }
- }
因此adb是事件驅動型,所有的事件調用fdevent_register進行注冊,該函數講事件保存到全局事件數組fd_table裡面。
C++代碼
- struct fdevent
- {
- fdevent *next;
- fdevent *prev;
-
- int fd;
- unsigned short state;
- unsigned short events;
-
- fd_func func;
- void *arg;
- };
如果有相關事件的到達則調用fun進行處理。
adb_main,即server已經啟動完成,再回到client的adb_commandline函數,我們繼續adb device命令的解析。
C++代碼
- 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;
- }
- }
它調用adb_query函數,參數是"host:devices“,它表示需要發往server的請求,這些請求分兩種query型和command型,分別調用adb_query和adb_command。
C++代碼
- char *adb_query(const char *service)
- {
- char buf[5];
- unsigned n;
- char *tmp;
-
- D("adb_query: %s\n", service);
- int fd = adb_connect(service);
-
- if(readx(fd, buf, 4)) goto oops;
-
- if(readx(fd, tmp, n) == 0) {
-
- }
- }
可以看到,它連接到server,返回一個描述符,然後直接從該描述符裡面讀取結果就可以了。看起來很簡單,adb_connect把下面很復雜的東西都包裝起來了。
adb_connect包裝了_adb_connect函數,包裝了一些adb server是否已經成功啟動,查詢adb server版本信息的工作,在_adb_connect中
調用socket_loopback_client(ADB_PORT, SOCK_STREAM);嘗試連接ADB_PORT,也就是5037,記住剛才adb server已經調用socket_loopback_server(port, SOCK_STREAM);這樣,client和service之間就可以開始通信了。請求信息“host:devices”將寫入adb server,來看adb server的處理函數ss_listener_event_func。
ss_listener_event_func創建一個local socket讀取該信息:
C++代碼
- fd = adb_socket_accept(_fd, &addr, &alen);
- if(fd < 0) return;
-
- adb_socket_setbufsize(fd, CHUNK_SIZE);
-
- s = create_local_socket(fd);
- if(s) {
- connect_to_smartsocket(s);
- return;
- }
先看create_local_socket,這個socket負責與client通信,回調處理函數是local_socket_event_func。
C++代碼
- asocket *create_local_socket(int fd)
- {
- asocket *s = calloc(1, sizeof(asocket));
- if(s == 0) fatal("cannot allocate socket");
- install_local_socket(s);
- s->fd = fd;
- s->enqueue = local_socket_enqueue;
- s->ready = local_socket_ready;
- s->close = local_socket_close;
-
- fdevent_install(&s->fde, fd, local_socket_event_func, s);
- return s;
- }
也就是由local_socket_event_func來讀取“host:devices”串,然後調用s->peer->enqueue(s->peer, p);交給對斷處理。
那local socket的對端是誰,看connect_to_smartsocket:
C++代碼
- void connect_to_smartsocket(asocket *s)
- {
- D("Connecting to smart socket \n");
- asocket *ss = create_smart_socket(smart_socket_action);
- s->peer = ss;
- ss->peer = s;
- s->ready(s);
- }
這裡明白了local socket的對端就是smart socket(remote socket的一種),與local socket交互。
C++代碼
- asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))
- {
- asocket *s = calloc(1, sizeof(asocket));
- if(s == 0) fatal("cannot allocate socket");
- s->id = 0;
- s->enqueue = smart_socket_enqueue;
- s->ready = smart_socket_ready;
- s->close = smart_socket_close;
- s->extra = action_cb;
-
- return s;
- }
這兩個socket結對以後,調用local socket的ready回調函數,也就是local_socket_ready。
C++代碼
- static void local_socket_ready(asocket *s)
- {
- fdevent_add(&s->fde, FDE_READ);
- }
意思是說,我(local socket)已經准備好接收你smart socket發過來的數據了。
那local socket調用的s->peer->enqueue(s->peer, p);就是smart_socket_enqueue。
在smart_socket_enqueue中,將剛剛讀取到的package插入到package列表中,然後解析service,即發過來的“host:devices”。
C++代碼
- #if ADB_HOST
- service = (char *)p->data + 4;
- if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {
- char* serial_end;
- service += strlen("host-serial:");
-
- // serial number should follow "host:"
- serial_end = strchr(service, ':');
- if (serial_end) {
- *serial_end = 0; // terminate string
- serial = service;
- service = serial_end + 1;
- }
- } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {
- ttype = kTransportUsb;
- service += strlen("host-usb:");
- } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {
- ttype = kTransportLocal;
- service += strlen("host-local:");
- } else if (!strncmp(service, "host:", strlen("host:"))) {
- ttype = kTransportAny;
- service += strlen("host:");
- } else {
- service = NULL;
- }
這裡將client發過來的請求,跟去前綴轉化為各種transport type,接著解析具體的service名稱,接著,調用handle_host_request處理一些可以立即響應的消息,然後直接返回(adb devices請求就是屬於這一種),,否則調用create_host_service_socket創建另外一個service socket作為local service的對段,而smart socket就沒什麼事了,可以關閉了,如下代碼。
C++代碼
- s2 = create_host_service_socket(service, serial);
- if(s2 == 0) {
- D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
- sendfailmsg(s->peer->fd, "unknown host service");
- goto fail;
- }
-
- adb_write(s->peer->fd, "OKAY", 4);
-
- s->peer->ready = local_socket_ready;
- s->peer->close = local_socket_close;
- s->peer->peer = s2;
- s2->peer = s->peer;
- s->peer = 0;
- D( "SS(%d): okay\n", s->id );
- s->close(s);
先來看可以用handle_host_request的部分,處理devices部分請求的代碼如下:
C++代碼
- // return a list of all connected devices
- if (!strcmp(service, "devices")) {
- char buffer[4096];
- memset(buf, 0, sizeof(buf));
- memset(buffer, 0, sizeof(buffer));
- D("Getting device list \n");
- list_transports(buffer, sizeof(buffer));
- snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);
- D("Wrote device list \n");
- writex(reply_fd, buf, strlen(buf));
- return 0;
- }
它講transport列表裡面的信息讀取出來,然後寫入reply_fd裡面,其實這裡猜也猜到了,它就是local socket的fd,也就是將信息寫入port5037裡面,這樣我們的client端就能將當前連接的設備信息打印到屏幕上了。
整個過程的如下圖: