Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發環境 >> Android開發工具ADB教程之二:HOST端

Android開發工具ADB教程之二:HOST端

編輯:Android開發環境

       大部分編程人員進行程序代碼分析時往往喜歡從main函數入手,因為它是程序入口,從main開始能夠更快更好的把握程序的整體結構。

       因此我們先來看adb.c的main函數的實現:

C++代碼
  1. int main(int argc, char **argv)   
  2. {   
  3.     adb_trace_init();   
  4. #if ADB_HOST   
  5.     adb_sysdeps_init();   
  6.     return adb_commandline(argc - 1, argv + 1);   
  7. #else   
  8.     if((argc > 1) && (!strcmp(argv[1],"recovery"))) {   
  9.         adb_device_banner = "recovery";   
  10.         recovery_mode = 1;   
  11.     }   
  12.   
  13.     start_device_log();   
  14.     return adb_main(0);   
  15. #endif   
  16. }  

       宏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++代碼
  1. void adb_set_transport(transport_type type, const char* serial)   
  2. {   
  3.        __adb_transport = type;   
  4.        __adb_serial = serial;   
  5. }  

       這兩個全局變量由client保存,將用來告訴server,與何種設備通信,用何種方式傳輸通信。

       接下來,adb_commandline用來判斷server守護進程是否已經啟動。

C++代碼
  1. if ((argc > 0) && (!strcmp(argv[0],"server"))) {   
  2.     if (no_daemon || is_daemon) {   
  3.         r = adb_main(is_daemon);   
  4.     } else {   
  5.         r = launch_server();   
  6.     }   
  7.     if(r) {   
  8.         fprintf(stderr,"* could not start server *\n");   
  9.     }   
  10.     return r;   
  11. }  

       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++代碼
  1. // child side of the fork   
  2.   
  3.  // redirect stderr to the pipe   
  4.  // we use stderr instead of stdout due to stdout's buffering behavior.   
  5.  adb_close(fd[0]);   
  6.  dup2(fd[1], STDERR_FILENO);   
  7.  adb_close(fd[1]);   
  8.   
  9.  // child process   
  10.  int result = execl(path, "adb", "fork-server", "server", NULL);   
  11.  // this should not return   
  12.  fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);  

       這裡子進程將STDERR_FILENO重定向到管道的寫端fd[1];然後講管道關閉,這樣所有對stderr的操作都將寫入父進程,fprintf語句只有在execl執行失敗時執行。

C++代碼
  1. // parent side of the fork   
  2.   
  3. char  temp[3];   
  4.   
  5. temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';   
  6. // wait for the "OK\n" message   
  7. adb_close(fd[1]);   
  8. int ret = adb_read(fd[0], temp, 3);   
  9. adb_close(fd[0]);   
  10. if (ret < 0) {   
  11.     fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", errno);   
  12.     return -1;   
  13. }   
  14. if (ret != 3 || temp[0] != 'O' || temp[1] != 'K' || temp[2] != '\n') {   
  15.     fprintf(stderr, "ADB server didn't ACK\n" );   
  16.     return -1;   
  17. }   
  18.   
  19. setsid();  

       父進程從管道的讀端讀取子進程發過來的應答,如果非“OK”,代表創建server失敗,返回,setsid用於避免父進程退出時子進程也退出,即server真正成為一個守護進程。

       花開兩朵各表一只,先看fork出來的子進程,即守護進程server,前面說到,它的啟動command是adb fork-server server,我們在回到adb的main函數,它用走到了adb_commandline裡面來,這時候它解析參數以後,is_daemon就變成1了,因此執行adb_daemon(is_daemon)。

C++代碼
  1. init_transport_registration();   
  2.   
  3.   
  4. ADB_HOST   
  5. HOST = 1;   
  6. usb_vendors_init();   
  7. usb_init();   
  8. local_init(ADB_LOCAL_TRANSPORT_PORT);   
  9.   
  10. if(install_listener("tcp:5037", "*smartsocket*", NULL)) {   
  11.     exit(1);   
  12. }   
  13. 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++代碼
  1. for (;;) {    
  2.     sleep(5);   
  3.     kick_disconnected();   
  4.     scan_usb_devices();   
  5. }  

       通過scan_usb_devices查找用於adb的usb設備:

       scan_usb_devices->check_device->register_device->register_usb_transport->init_usb_transport->register_transport。

       在init_usb_transport中:

C++代碼
  1. void init_usb_transport(atransport *t, usb_handle *h, int state)   
  2. {      
  3.     D("transport: usb\n");   
  4.     t->close = remote_close;   
  5.     t->kick = remote_kick;   
  6.     t->read_from_remote = remote_read;   
  7.     t->write_to_remote = remote_write;   
  8.     t->sync_token = 1;   
  9.     t->connection_state = state;   
  10.     t->type = kTransportUsb;   
  11.     t->usb = h;   
  12.   
  13. #if ADB_HOST   
  14.     HOST = 1;   
  15. #else   
  16.     HOST = 0;   
  17. #endif     
  18. }            

       可以看到,不管在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++代碼
  1. int init_socket_transport(atransport *t, int s, int port, int local)   
  2. {          
  3.     int  fail = 0;   
  4.   
  5.     t->kick = remote_kick;   
  6.     t->close = remote_close;   
  7.     t->read_from_remote = remote_read;   
  8.     t->write_to_remote = remote_write;   
  9.     t->sfd = s;   
  10.     t->sync_token = 1;   
  11.     t->connection_state = CS_OFFLINE;   
  12.     t->type = kTransportLocal;   
  13.            
  14. #if ADB_HOST   
  15.     if (HOST && local) {   
  16.         adb_mutex_lock( &local_transports_lock );   
  17.         {    
  18.             int  index = (port - ADB_LOCAL_TRANSPORT_PORT)/2;   
  19.        
  20.             if (!(port & 1) || index < 0 || index >= ADB_LOCAL_TRANSPORT_MAX) {   
  21.                 D("bad local transport port number: %d\n", port);   
  22.                 fail = -1;   
  23.             }   
  24.             else if (local_transports[index] != NULL) {   
  25.                 D("local transport for port %d already registered (%p)?\n",   
  26.                 port, local_transports[index]);   
  27.                 fail = -1;   
  28.             }   
  29.             else  
  30.                 local_transports[index] = t;   
  31.         }   
  32.         adb_mutex_unlock( &local_transports_lock );   
  33.     }   
  34. #endif   
  35.     return fail;   
  36. }  

       注意看ADB_HOST裡面的東西,如果是在HOST端,則將transport添加到列表裡面,因為adb device就是從這個列表裡面讀信息的。

       再看register_transport,它將transport信息,一個tmsp的結構體,寫入transport_registration_send。

C++代碼
  1. struct tmsg    
  2. {   
  3.     atransport *transport;   
  4.     int         action;   
  5. };    

       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++代碼
  1. p = get_apacket();   
  2. p->msg.command = A_SYNC;                                                     
  3. p->msg.arg0 = 1;   
  4. p->msg.arg1 = ++(t->sync_token);   
  5. p->msg.magic = A_SYNC ^ 0xffffffff;                                          
  6. if(write_packet(t->fd, &p)) {                                                
  7.     put_apacket(p);   
  8.     D("from_remote: failed to write SYNC apacket to transport %p", t);       
  9.     goto oops;                                                               
  10. }      

      首先向fd寫入一個包含A_SYNC命令的信息包,用於同步,transport_socket的處理回調函數transport_socket_events會執行,繼而調用handle_packet處理信息包。

C++代碼
  1. case A_SYNC:                                                                 
  2.     if(p->msg.arg0){                                                         
  3.         send_packet(p, t);                                                   
  4.         if(HOST) send_connect(t);                                            
  5.     } else {                                                                 
  6.         t->connection_state = CS_OFFLINE;                                    
  7.         handle_offline(t);                                                   
  8.         send_packet(p, t);                                                   
  9.     }                                                                        
  10.     return;      

       handle_packet判斷是A_SYNC同步命令,則同時將信息包發送給遠端,並發送一個連接請求給遠端,裡面包含adb版本,最大載荷等信息。send_package講信息包寫回給transport_socket。

       回過頭來output_thread線程回收到這些信息包,並將這些包寫入遠端。

       寫的太深入了,回到adb_main函數,初始化完可能USB和emulator的transport之後,執行下面這段代碼:

C++代碼
  1. if(install_listener("tcp:5037", "*smartsocket*", NULL)) {   
  2.     exit(1);   
  3. }  

       listener是一個很重要的概念,它綁定到一個本地端口,即local socket,負責與client通信(稍候將看到),並且創建一個連接到遠端的remote socket,smartsocket是一個特殊的socket,它其實類似一個接線員的角色,它分析後看你要連接到哪個remote socket,然後幫你連上。注意這裡的第三個參數NULL,因為接線員還來不及分析你的adb命令參數,不知道你要往哪個remote上連,所以這裡為NULL,等分析好了,確定要連接那個remote socket,smartsocket的任務也完成了。

C++代碼
  1. struct alistener   
  2. {   
  3.     alistener *next;   
  4.     alistener *prev;   
  5.   
  6.     fdevent fde;   
  7.     int fd;   
  8.   
  9.     const char *local_name;   
  10.     const char *connect_to;   
  11.     atransport *transport;   
  12.     adisconnect  disconnect;   
  13. };  

       在install_listener裡面:

C++代碼
  1. l->fd = local_name_to_fd(local_name);   
  2. close_on_exec(l->fd);   
  3. if(!strcmp(l->connect_to, "*smartsocket*")) {   
  4.     fdevent_install(&l->fde, l->fd, ss_listener_event_func, l);   
  5. }   

       這樣,client來消息的時候,就可以調用ss_listener_event_func進行處理了。

       接下來,adb_main執行下面代碼:

C++代碼
  1. if (is_daemon)   
  2. {   
  3.     // inform our parent that we are up and running.   
  4. f defined(HAVE_FORKEXEC)   
  5.     fprintf(stderr, "OK\n");   
  6. if  
  7.     start_logging();   
  8. }  

       這段代碼,告訴父進程adb server已經跑起來了,因此,往stderr裡面寫一個OK,還記得剛剛server已經將stderr重定向到fd[1]了,所以父進程能接收到這個OK消息。

       接下來,server調用fdevent_loop進入事件循環。

C++代碼
  1. for(;;) {   
  2.     fdevent_process();   
  3.   
  4.     while((fde = fdevent_plist_dequeue())) {   
  5.         unsigned events = fde->events;   
  6.         fde->events = 0;   
  7.         fde->state &= (~FDE_PENDING);   
  8.         dump_fde(fde, "callback");   
  9.         fde->func(fde->fd, events, fde->arg);   
  10.     }   
  11. }  

       因此adb是事件驅動型,所有的事件調用fdevent_register進行注冊,該函數講事件保存到全局事件數組fd_table裡面。

C++代碼
  1. struct fdevent   
  2. {   
  3.     fdevent *next;   
  4.     fdevent *prev;   
  5.   
  6.     int fd;   
  7.     unsigned short state;   
  8.     unsigned short events;   
  9.   
  10.     fd_func func;   
  11.     void *arg;   
  12. };  

       如果有相關事件的到達則調用fun進行處理。

       adb_main,即server已經啟動完成,再回到client的adb_commandline函數,我們繼續adb device命令的解析。

C++代碼
  1. if(!strcmp(argv[0], "devices")) {   
  2.     char *tmp;   
  3.     snprintf(buf, sizeof buf, "host:%s", argv[0]);   
  4.     tmp = adb_query(buf);   
  5.     if(tmp) {   
  6.         printf("List of devices attached \n");   
  7.         printf("%s\n", tmp);   
  8.         return 0;   
  9.     } else {   
  10.         return 1;   
  11.     }   
  12. }  

       它調用adb_query函數,參數是"host:devices“,它表示需要發往server的請求,這些請求分兩種query型和command型,分別調用adb_query和adb_command。

C++代碼
  1. char *adb_query(const char *service)   
  2. {          
  3.     char buf[5];   
  4.     unsigned n;   
  5.     char *tmp;   
  6.   
  7.     D("adb_query: %s\n", service);    
  8.     int fd = adb_connect(service);   
  9.   
  10.     if(readx(fd, buf, 4)) goto oops;   
  11.   
  12.     if(readx(fd, tmp, n) == 0) {   
  13.   
  14.     }   
  15. }  

       可以看到,它連接到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++代碼
  1. fd = adb_socket_accept(_fd, &addr, &alen);   
  2. if(fd < 0) return;   
  3.   
  4. adb_socket_setbufsize(fd, CHUNK_SIZE);   
  5.   
  6. s = create_local_socket(fd);   
  7. if(s) {   
  8.     connect_to_smartsocket(s);   
  9.     return;   
  10. }  

       先看create_local_socket,這個socket負責與client通信,回調處理函數是local_socket_event_func。

C++代碼
  1. asocket *create_local_socket(int fd)   
  2. {          
  3.     asocket *s = calloc(1, sizeof(asocket));   
  4.     if(s == 0) fatal("cannot allocate socket");   
  5.     install_local_socket(s);   
  6.     s->fd = fd;   
  7.     s->enqueue = local_socket_enqueue;   
  8.     s->ready = local_socket_ready;   
  9.     s->close = local_socket_close;   
  10.            
  11.     fdevent_install(&s->fde, fd, local_socket_event_func, s);   
  12.     return s;   
  13. }  

       也就是由local_socket_event_func來讀取“host:devices”串,然後調用s->peer->enqueue(s->peer, p);交給對斷處理。

       那local socket的對端是誰,看connect_to_smartsocket:

C++代碼
  1. void connect_to_smartsocket(asocket *s)   
  2. {   
  3.     D("Connecting to smart socket \n");   
  4.     asocket *ss = create_smart_socket(smart_socket_action);   
  5.     s->peer = ss;   
  6.     ss->peer = s;   
  7.     s->ready(s);   
  8. }  

       這裡明白了local socket的對端就是smart socket(remote socket的一種),與local socket交互。

C++代碼
  1. asocket *create_smart_socket(void (*action_cb)(asocket *s, const char *act))   
  2. {          
  3.     asocket *s = calloc(1, sizeof(asocket));   
  4.     if(s == 0) fatal("cannot allocate socket");   
  5.     s->id = 0;   
  6.     s->enqueue = smart_socket_enqueue;   
  7.     s->ready = smart_socket_ready;   
  8.     s->close = smart_socket_close;   
  9.     s->extra = action_cb;   
  10.   
  11.     return s;    
  12. }     

       這兩個socket結對以後,調用local socket的ready回調函數,也就是local_socket_ready。

C++代碼
  1. static void local_socket_ready(asocket *s)   
  2. {   
  3.     fdevent_add(&s->fde, FDE_READ);   
  4. }  

       意思是說,我(local socket)已經准備好接收你smart socket發過來的數據了。

       那local socket調用的s->peer->enqueue(s->peer, p);就是smart_socket_enqueue。

       在smart_socket_enqueue中,將剛剛讀取到的package插入到package列表中,然後解析service,即發過來的“host:devices”。

C++代碼
  1. #if ADB_HOST   
  2.     service = (char *)p->data + 4;   
  3.     if(!strncmp(service, "host-serial:", strlen("host-serial:"))) {   
  4.         char* serial_end;   
  5.         service += strlen("host-serial:");   
  6.   
  7.         // serial number should follow "host:"   
  8.         serial_end = strchr(service, ':');   
  9.         if (serial_end) {   
  10.             *serial_end = 0; // terminate string   
  11.             serial = service;   
  12.             service = serial_end + 1;   
  13.         }   
  14.     } else if (!strncmp(service, "host-usb:", strlen("host-usb:"))) {   
  15.         ttype = kTransportUsb;   
  16.         service += strlen("host-usb:");   
  17.     } else if (!strncmp(service, "host-local:", strlen("host-local:"))) {   
  18.         ttype = kTransportLocal;   
  19.         service += strlen("host-local:");   
  20.     } else if (!strncmp(service, "host:", strlen("host:"))) {   
  21.         ttype = kTransportAny;   
  22.         service += strlen("host:");   
  23.     } else {   
  24.         service = NULL;   
  25.     }  

       這裡將client發過來的請求,跟去前綴轉化為各種transport type,接著解析具體的service名稱,接著,調用handle_host_request處理一些可以立即響應的消息,然後直接返回(adb devices請求就是屬於這一種),,否則調用create_host_service_socket創建另外一個service socket作為local service的對段,而smart socket就沒什麼事了,可以關閉了,如下代碼。

C++代碼
  1. s2 = create_host_service_socket(service, serial);   
  2. if(s2 == 0) {   
  3.     D( "SS(%d): couldn't create host service '%s'\n", s->id, service );   
  4.     sendfailmsg(s->peer->fd, "unknown host service");   
  5.     goto fail;   
  6. }   
  7.   
  8. adb_write(s->peer->fd, "OKAY", 4);   
  9.   
  10. s->peer->ready = local_socket_ready;   
  11. s->peer->close = local_socket_close;   
  12. s->peer->peer = s2;   
  13. s2->peer = s->peer;   
  14. s->peer = 0;   
  15. D( "SS(%d): okay\n", s->id );   
  16. s->close(s);  

       先來看可以用handle_host_request的部分,處理devices部分請求的代碼如下:

C++代碼
  1. // return a list of all connected devices   
  2. if (!strcmp(service, "devices")) {   
  3.     char buffer[4096];   
  4.     memset(buf, 0, sizeof(buf));   
  5.     memset(buffer, 0, sizeof(buffer));   
  6.     D("Getting device list \n");   
  7.     list_transports(buffer, sizeof(buffer));   
  8.     snprintf(buf, sizeof(buf), "OKAY%04x%s",(unsigned)strlen(buffer),buffer);   
  9.     D("Wrote device list \n");   
  10.     writex(reply_fd, buf, strlen(buf));   
  11.     return 0;   
  12. }  

       它講transport列表裡面的信息讀取出來,然後寫入reply_fd裡面,其實這裡猜也猜到了,它就是local socket的fd,也就是將信息寫入port5037裡面,這樣我們的client端就能將當前連接的設備信息打印到屏幕上了。

 

       整個過程的如下圖:

Android開發工具ADB教程之二:HOST端

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved