宅男
分类: LINUX
2012-12-07 14:40:33
最近有个项目需要修改ADB,对ADB的代码进行了阅读和分析。以Android4.0ICS的源码为例,ADB的代码在/system/core/adb中。
先看Android.mk文件,在这个工程中,一共可以编译出三个模块:1,PC机端的ADB,针对不同的操作系统平台,所包含的文件不一样,具体可看Android.mk文件;2终端(这里所谓的终端就是手机)上的ADBD,作为一个daemon在终端上运行的;3终端上的ADB,作为host端的ADB连接,一般用不到。在编译的时候当ADB_HOST=1时,表明的是编译的HOSTADB,当ADB_HOST=0时,表示编译的SlaveADB,即ADBD。在代码中可以看到有很多宏使用这个变量来区分主从的代码。
在分析代码之前先说下ADB中采用的技术和实现方法,在ADB中大量运用了socket编程,进程间,线程间的数据交互都是通过网络传输来实现的。PC端和终端的传输也是最后抽象为TCP的传输,用的端口是5037。USB的传输与5037端口的传输进行绑定,即向端口5037的读写就是对USB的读写。
ADB的数据连接实现可以分为三层:1,USB层,通过读USB接口的信息,确定连接,对USB接口进行读写,向上虚拟为5037端口;2传输层(见transport.c,fdevent.c),负责管理上层的通道连接和下层对5037端口的连接,其实就是将多个连接统一入口传给5037端口,使得USB层进行传输,这里是进行双向的传输;3socket层,建立通道连接,对传输协议做一个包装,见socket.c中。
鉴于ADB的多样化,他可以于虚拟设备进行连接,所以在这里我们只介绍通过USB接口进行连接的ADB协议实现。并且关于ADBHOST的功能如LOGCAT,SHELL的实现可以自己看,我也没看啊,这里直介绍底层的通信实现。
下面我们就从万物的核心main中开始分析ADB这个东东是怎么运转起来的。首先我们看HOST端,在前期的初始化中两端有些不同,但是到后期的等待数据,发送数据的时候,其结构就类似了。
ADBHOST
首先我们可以打开adb.c中的最后一个函数就是main函数了,很简单的几句话,现在我们只看ADB_HOST包含的内容。可以看到:
1276 adb_sysdeps_init();
1277 adb_trace_init();
1278 D("Handling commandline()\n");
1279 return adb_commandline(argc - 1, argv + 1)
很简单的四句话,前两句是在对LOG的一些初始化,这些我们可以忽略,这里值得说的是ADB的LOG体系,设计地不错,通过对宏的修改,或者是环境变量的设置,可以设置LOG的模块等级,有兴趣的同学可以看看log相关的内容。这里面主要做事的是adb_commandline这句,我们顺藤摸瓜,找着这个函数的定义在commmandline.c中的822行。这函数的前面是对输入参数的判断并相应的赋值给内部使用信号,如什么is_server,is_daemon,product等等。这些内部信号,使用的时候就知道他是什么意思了,或者直接从变量名也能看出来是看啥的。在这里插一句ADBHOST的实现方式,大致是这样的,当ADB启动时,在第一次连接的时候,ADB会去启动一个server,注意这个就类似于daemon,如果不手动杀死它,它会一直运行的,它是负责对USB接口的监听,并建立5037端口的server,共各client进行connect。话题又扯原来,咱们再看看adb_commandline里面的内容吧。在913行,发现有个直接调用adb_main的和launch_server的,当然必须是is_server==1才行啊,这里我们先不用管这俩分支,再往下看,发现貌似是ADB命令的参数啊,当然第一个是最容易的devices,这不是获取设备信息吗,那我们就看这个简单的,反正向下看只是参数不一样,但是流程应该都是一样的。我们看932-943行,发现值得注意的就剩下了adb_query,看来这个还是挺重要的,那我们就link到它的实现吧。
话说我们现在移驾到了adb_query,他在adb_client.c中,在这个函数没几行后,又一个操作让我们眼前一亮,adb_connect,我靠,终于找着个连接了,它会返回一个文件描述符,接下来就是对文件描述符的读写了,那我们断定这个adb_connect就是完成了主从的连接啊,别看这么不起眼,但是他可做了很多事,真是人不可貌相啊。那我们就在深入参观这个adb_connect了,索性他就在adb_client里面,第一行就把我给镇住了,它又来了个_adb_connect,看着函数的第一个字母为_,我很欣慰,原来google工程也会遵循这个“潜规则”,内部函数一般都是以”_”开头,看来我们已经打入内部了啊。那干脆就继续深入呗,这样我们就到了_adb_connect中,通过我们对上面代码的分析,我们已经有火眼金睛了,直奔到socket_loopback_client,为了给大家点惊喜我来个剧透吧,可以看到这个函数的第一个参数是个port,一看就是tcp的port嘛,那他是干啥的呢,自己去找或者我来说说我的理解吧,就是建立一个client与这个port的连接,那个这个port是啥呢----5037,哦那就明白了。可以我们还没有建立这个种服务呢,那怎么办啊,这简单,这个就返回错误了呗,那返回错误_adb_connect就返回-2了呗,哎呀,终于我们的堆栈少了许多,那我们就继续看adb_connect了,一看返回-2,这号啊,进入adb_client.c中的216行了,然后有个标签很是响亮--start_server,我们这时终于找着了组织啊,然后就直接launch_server了,这个我们似曾相识啊,原来开个server如此容易直接launch_server就可以了,哈哈哈。
经过山路十八弯我们又到了adb.c中的launch_server,看来这个adb.c深藏不漏啊,刚说简单,就到了这个不简单了。这里面就要说下我们的google工程师了,针对不同的平台可以做一层适配层吗,干嘛在这还有个宏来区分代码呢,可能工程师也想偷懒吧。不管咋样,我们继续往下看,对于Linux编程人员当然是看linux系统下的实现了,直奔773行。这里就有些端倪了,fork了一个进程,哈哈,终于找着这个server的开始的地方了,毫不犹豫去了它的子进程,pid==0的地方,这里用了个管道,将子进程的错误输出传给了父进程?至今我没想明白,它为啥用错误输出呢,哎。之后看着了我们亲爱的execl,话说这个path就是我们的adb程序,看来还是走我们的main来实现的啊,值得注意的是,加了三个参数,adb,fork-server,server.然后我们再看看父进程,父进程是要等子进程的信息啊,它要等到子进程给他发OK,那我们就等着子进程吧。
话说子进程重新进入main后,直奔commandline,其中adb这个参数被斩断,只剩fork-server,server俩个参数。这时我们又来到了亲切的adb_commandline函数,发现两个参数派上用场了,直接将is_server,is_daemon赋值1了,一看,这个走到adb_main这个分支了,哈哈,看来好戏开场了。它带了is_daemon,server_port。显然is_daemon==1,server_port =ADB_DEFAULT_PORT。
无独有偶,ADBSlave也是走到了这个函数里面,不过,他的第一个参数是0。不行我们得加快速度啊,从adb_main开始我们就要取其精华,去其糟粕啊。
854 init_transport_registration();看实现,很简单
657 int s[2];
658
659 if(adb_socketpair(s)){
660 fatal_errno("cannotopen transport registration socketpair");
661 }
662
663 transport_registration_send= s[0];
664 transport_registration_recv= s[1];
665
666 fdevent_install(&transport_registration_fde,
667 transport_registration_recv,
668 transport_registration_func,
669 0);
670
671 fdevent_set(&transport_registration_fde, FDE_READ)
初始化传输注册?这个ADBSlave端也有。这里我们学习下几个知识点:
Adb_socketpair,这是向操作系统申请一对socket,给他们俩个端口建立一个管道,供通信。
Fdevent_install:这个要想了解详细的内容的话,可以看看fdevent.c文件,他其实就是文件监听事件管理,只要利用fdevent_install注册到他的管理后,当注册的文件有里面有活动的时候,表述有读或者是写,这里是transport_registration_recv,就会触发func,这里的func就是transport_registration_func。
这俩连起来来看就是只要transport_registration_send发送数据,就会触发函数transport_registration_func。那我们就有必要看看transport_registration_func这个函数了。至于啥时候发送数据,那待稍后分解。
Transport_registration_func这个函数待遇真牛,我们得另来一段了。
578 if(transport_read_action(_fd, &m)) {
这个就是取数据了,我们就不做介绍了。
617 if(adb_socketpair(s)) {
625
626 fdevent_install(&(t->transport_fde),
627 t->transport_socket,
628 transport_socket_events,
629 t);
630
631 fdevent_set(&(t->transport_fde), FDE_READ);
632
633 if(adb_thread_create(&input_thread_ptr, input_thread,t)){
634 fatal_errno("cannot create input thread");
635 }
636
637 if(adb_thread_create(&output_thread_ptr, output_thread,t)){
638 fatal_errno("cannot create output thread");
639 }
这可是这个函数的重点啊,这里插一句我们看代码的时候一定要心无旁骛,直接切中要害,找单一正确的分支走下去,这就像是走迷宫一样,真相只有一个。所以我们来到了这个分支。
经过上面的介绍这个个也不足为奇了,但是下面create两个线程,这个就和USB有关系啦,终于衔接上了,这两个线程分别对USB的读和写,数据就是从这个t->transport_socket来的。那我们看看transport_socket_events吧,这就开始处理从USB来的数据喽,独到数据后给了handle_packet。发现了ADB的底层协议通信,如果想进一步了解可以看看TRANSPORT.TXT文档。
858 HOST = 1;
859 usb_vendors_init();//关于VENDORS的过滤,
860 usb_init();
861 local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);//内部端口,不考虑
好了,我们再看看USB的实现了,那就剩下了usb_init了。那我们继续找linux下的usb_init实现喽。这个是在usb_linux.c中,这里其线程对USB设备进行查看,找到适合的USB设备进行register_devices.填充usb_handle数据结构,加入链表中。注意,里面的注册会调用register_usb_transport。这里就会产生一个atransport,-》register_transport。然后向transport_registration_send发送命令,这就和上面衔接上了,transport_socket_events正式启动。
865 if(install_listener(local_name, "*smartsocket*", NULL))
这个调用可是ADBHOST端server最后的重要函数了。在这里面申请一个alistener对象,建立监听5037端口的server,但有数据到来时,就建立一个socket,具体可以查看ss_listener_event_func函数的实现。
下面我们看看adb.c:966行,我们进入这个if后,向异常输出为OK,可以看到我们前面创建子进程后,父进程就是在等这个信息,终于等到了,内流满面啊。
接下来是
979 fdevent_loop();
这个是在一看就是在里面实现了循环,它就是在fdevent_install后,然后对这些文件描述符进行监听,然后处理的过程。我们先放放,且看ADBSlave的实现。
ADBSlave的实现
话说天下大事,合久必分,分久必合。ADBSlave在进入main之后就和ADBHost分道扬镳,收敛于adb_main,然而我们的ADBHOST Server也会执行到这边来,所以我们可是轻车熟路啊。且看ADBSlave adb_main的执行。
854 init_transport_registration();
这不和HOST端一样,那功能自然也是一样一样的。然后后面的什么secure,prop之类的我们可以直接略过了。哈哈来到了938行
938 if(install_listener(local_name, "*smartsocket*",NULL)) {
不说了,这个和主端也是一样一样的,下面直接到USB的初始化
956 usb_init();这个usb_init的实现是在usb_linux_client.c中,打开/dev/android_adb设备,这个设备文件时内核暴露出来和应用通信的ADB驱动接口。我们对ADB的数据,最终是通过写,读这个文件实现的,可以看到,在usb_open_thread中,调用
register_usb_transport来实现对上层的注册,从而上层得到一个atransport。
最后我们来看看fdevent_loop,这是ADB,ADBD大多数时间工作的地方。前面的一大推都是为了这个做准备的。
684 fdevent_subproc_setup();
685
686 for(;;) {
687 D("--- ---- waiting for events\n");
688
689 fdevent_process();
690
691 while((fde = fdevent_plist_dequeue())) {
692 fdevent_call_fdfunc(fde);
693 }
694 }
很简单的几句话,fdevent_subproc_setup又是注册了一个fdevent,这个是接受推出信息的,我们就不看他了。我们只看看fdevent_process(),这函数就是查看fdevent中的信息到来,如果有信息,将信息入队列,然后再691行出队列,进而执行它的fdevent_call_fdfunc函数。
还有关于sockets.do_cmd的一些功能没有详细阐述,相信以上对你有用。由于电脑上没有VIso,所以懒得画图了,ADB就到此为止了。