注册完三个通信函数和连个线程之后,就调用函数install_listener在5037端口进行监听,然后将这个服务添加到链表中(可提供多个服务,这个我不关注),并将输入流重定向到/dev/null,打印 adb start ,向之前打开的管道写端写入 OK\N,通知服务已经启动。进入fdevent_loop,fdevent_process进行处理,这两个函数后面进行分析。回过头来看adb command发送的adb shell命令。
前文讲到子进程execl了adb命令,我们回过头来看看父进程做什么
chartemp[3];
temp[0] = 'A'; temp[1] = 'B'; temp[2] = 'C';
// wait for the "OK\n" message
adb_close(fd[1]);
intret = adb_read(fd[0], temp, 3);
intsaved_errno = errno;
adb_close(fd[0]);
if(ret < 0) {
fprintf(stderr, "could not read ok from ADB Server, errno = %d\n", saved_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;
}
首先父进程关闭了管道写端,准备从管道读端读取数据,当adb-server启动后向写端写入OK\n 读端从阻塞返回,launch_server返回。再次进行__adb_connect 之后调用SendProtocolString 发送 000dhost:features,成功返回文件描述符,adb_connect返回到adb_query,
, 之后读取四个字节长度,然后根据长度读取数据z,这里返回000c也就是12, 后面返回的是shell_v2,cmd
协议版本和命令类型。adb_query返回之后adb_get_feature_set返回, FeatureSet
为 shell_v2,cmd.
之后调用CanUseFeature 对一下版本是否匹配。 最后解析参数调用RemoteShell函数进行处理。
然后发送RemoteShell :shell,v2,TERM=xterm:
之后就是从STDIN中读取数据 写入到5037端口的链接。 这部分看完了,再回来看adb-server收到接到adb的链接请求后如何处理。
if(ev &FDE_READ) {
sockaddr_storagess;
sockaddr* addrp =reinterpret_cast(&ss);
socklen_talen =sizeof(ss);
intfd = adb_socket_accept(_fd,addrp,&alen);
if(fd <0)return;
intrcv_buf_size =CHUNK_SIZE;
adb_setsockopt(fd,SOL_SOCKET,SO_RCVBUF,&rcv_buf_size,sizeof(rcv_buf_size));
asocket* s = create_local_socket(fd);
if(s) {
connect_to_smartsocket(s);
return;
}
adb_close(fd);
}
adb-server在ss_listener_event_func观察可读事件,当链接建立后进入这个函数,讲请求接进来后 重新设置缓冲区。
创建一对asocket一个代表服务端,一个代表客户端(其实是一个从接进来的链接中读取数据,一个写入数据)。
我们来看看怎么处理的,这个流程各种回调,响应式变成,好无聊
create_local_socket 创建客户端asocket 设置回调函数local_socket_enqueue local_socket_ready local_socket_close 链入local_socket_list
connect_to_smartsocket 创建服务端asocket,设置回调函数smart_socket_enqueue smart_socket_ready smart_socket_close,设置local和smatrserver互为对端。
下面称服务端asocket和客户端socket分别为local_asocket,server_asocket,
最后执行local_asocket.ready(local_asocket),
static voidlocal_socket_ready(asocket* s) {
/* far side is ready for data, pay attention to
readable events */
fdevent_add(&s->fde,FDE_READ);
}
其实就是观察这个socket可写状态(可写回调local_socket_event_func),因为客户端马上写了个000dhost:features(前边是长度,后边是命令)。所以该函数会马上回调。
这时候关注读事件部分
if(ev &FDE_READ) {
这时候还没有设置transport 当前的max_payload= 256*1024,从客户端套接字读取数据到apacket->data,执行server_asocket.enqueue
(server_asocket,p),注意这里p存了从套接字中读取的数据,server_asocket.enqueue函数就是smart_socket_enqueue函数,可以猜测smart_socket_enqueue是写入数据到usb,然后返回数据后写出到fd,这里稍后分析,再看smart_socket_enqueue 返回后,就卸载了可读事件的回调,因为之后要写入数据了。
现在分析smart_socket_enqueue函数:获取数据前四位长度。解析命令,之后对不同命令进行处理,这里的命令是host:features,这里将
type设置成kTransportAny,server设置成features,调用handle_host_request处理请求,在adb.cpp中,
if(!strncmp(service, "transport", strlen("transport"))) {
TransportType type = kTransportAny;
atransport* t = acquire_one_transport(type, serial,nullptr, &error);
。。。
s->transport = t;
SendOkay(reply_fd);
通过acquire_one_transport获取一个transport, 从transport_list中找出合适的atransport,根据类型(还记得我们之前打开的套接字对用于和usb通信的那套通信框架吗,atransport就描述了该结构)对于不同的类型还有写限制。 返回到handle_host_request
,获取了atransport后,设置server_asocke的transport为 (选定通信通道),调用SendOkay(reply_fd);这里的reply_fd就是链接如5037端口的套接字,SendOkay就是向套接字中写入OKAY.
这里我们不在关心协议细节的东西,我们来查看如何通信。假如命令是transport直接返回,由此可见transport是用于找到通信介质。这种命令不需要写入usb,就直接返回了。再看features命令,调用SendOkay(reply_fd, FeatureSetToString(t->features()));函数。
static intSendOkay(intfd,conststd::string& s) {
SendOkay(fd);
SendProtocolString(fd, s);
return0;
}
除了发送ok以为,还发送了 SendProtocolString 发送协议信息。到这里还没有看到如何和adbd通信。 整个流程还没有跑通,
关键点就在transport初始化的时候,会初始化一些信息 feure也是其中初始化的数据。 所以features 也是不需要和手机通信的,处理完成这些不用和手机进行通信的请求后就是handle_forward_request 转发请求, 也是获取相应的atransport 然后install_listeren,
以RemoteShell :shell,v2,TERM=xterm:这条命令为例子 第一个参数是RemoteShell,第二个参数是shell,v2,TERM=xterm: 三个参数是transport
创建新的 alistener
connect_to=shell,v2,TERM=xterm:
name = RemoteShell
网友评论