前言
前文
ADB(一)_概况了解
ADB(二)_ADBD_main()函数代码梳理
ADB(三)_ADBD_adbd_main()函数代码梳理
ADB(四)_host端的启动流程代码梳理
ADB(五)_host端adb server相关的代码梳理
ADB(六)_调试ADB(ADB设置自身日志的代码梳理和设置ADB自身日志可见)
ADB(七)_USB连接 (ABD通过USB连接的流程分析)
我们已经知道,ADB经常使用的是USB连接Android开发设备,不过ADB不光只能使用USB来连接设备。它还可以使用WiFi来链接局域网内的设备,就这为某些设备在不方便使用USB的情况提供了巨大的便利。
今天我们主要针对host端和Android设备通过TCP连接的情况来说明,这里就当是大家已经对TCP通信有所了解,至少是知道在开发中的创建一个使用C++实现的C/S的socket通信的简单过程。
首先,我们从Android 设备端说起,因为在这个过程中,我们的Android设备就相当于一个TCP通信的Server,而我们的host端运行的adb就是通信的Client.
在正式对adbd的代码进行梳理之前,我们先对TCP通信编程的步骤进行简单的介绍,
- tcp编程的 server端
- 调用socket()函数来创建socket
- 调用setsockopt()函数来设置相关属性
- 调用bind()函数绑定IP和端口信息到socket上
- 调用listen()函数来开启监听
- 调用accept()函数来接受client端的通信请求
- 调用send()/write()函数来发送数据;调用reve()/read()函数来接受数据
- 调用close()/shutdown()函数来关闭通信
- tcp编程的 client 端
- 调用socket()函数来创建socket
- 调用setsockopt()函数来设置相关属性
- 调用bind()函数绑定IP和端口信息到socket上
- 调用connect()函数来连接server端
- 调用send()/write()函数来发送数据;调用reve()/read()函数来接受数据
- 调用close()/shutdown()函数来关闭通信
Android设备端的TCP连接 初始化和传输处理流程
在adbd的初始化过程中,就在adbd_main()函数中是有对tcp通信初始化的,我们现在就来看看:
int adbd_main(int server_port) {
...
std::string prop_port = android::base::GetProperty("service.adb.
tcp.port", "");
if (prop_port.empty()) {
prop_port = android::base::GetProperty("persist.adb.tcp.port", "");
}
int port;
if (sscanf(prop_port.c_str(), "%d", &port) == 1 && port > 0) {
setup_port(port);
} else if (!is_usb) {
setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
// DEFAULT_ADB_LOCAL_TRANSPORT_PORT =5555;
}
...
return 0;
}
我们可以看到,adbd会首先读取Android的系统属性"service.adb.
tcp.port",这个属性就是用于我们的adbd进行tcp通信的端口设置,如果此端口被设置,我们就会使用设置的端口来初始化我们的tcp,如果没有设置,并且当前不是使用usb通信的,那么就会使用默认的端口DEFAULT_ADB_LOCAL_TRANSPORT_PORT = 5555
来初始化tcp,不管使用设置的端口还是使用默认的端口,接下来都会调用setup_port()函数,我们就到setup_port()函数中一探究竟:
1. setup_port()
static void setup_port(int port) {
local_init(port);
setup_mdns(port);
}
setup_port()函数内部只要就是调用了两个函数,第一个就是我们的tcp传输设置的local_init()函数.第二个是Android组播域名服务设置的setup_mdns(),我们主要对第一个函数进行说明:
2. local_init()
void local_init(int port)
{
...
func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
debug_name = "server";
...
std::thread(func, port).detach();
}
local_init()函数内部也很简单,首先通过调用use_qemu_goldfish()判断是emulator还是Android设备中,当前我们分析的是Android设备端的代码,所以这里我们需要的func为server_socket_thread(),然后,启动一个新的进程专门用来执行server_socket_thread()函数,并且传入端口号.
3.server_socket_thread()
接下来我们就要看看server_socket_thread()的实现是怎么样子的:
static void server_socket_thread(int port) {
int serverfd, fd;
serverfd = -1;
for(;;) {
if(serverfd == -1) {
std::string error;
serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
if(serverfd < 0) {
std::this_thread::sleep_for(1s);
continue;
}
close_on_exec(serverfd);
}
fd = adb_socket_accept(serverfd, nullptr, nullptr);
if(fd >= 0) {
...
std::string serial = android::base::StringPrintf("host-%d", fd);
if (register_socket_transport(fd, serial.c_str(), port, 1) != 0) {
adb_close(fd);
}
}
}
}
如上代码所示:server_socket_thread()函数主要就是处理tcp通信中server端的初始化,先概括一下函数内部的处理逻辑:
- 首先函数进入一个大的死循环中,然后通过调用network_inaddr_any_server()函数来创建server端的socket.
- 调用 adb_socket_accept()来接受client端的请求信息,然后fd.用于tcp的通信
- 最后就调用register_socket_transport()函数来注册TCP传输事件.
接下来,我们就对上述的三个处理部分进行详细说明:
3.1 network_inaddr_any_server()
inline int network_inaddr_any_server(int port, int type, std::string* error) {
return _fd_set_error_str(socket_inaddr_any_server(port, type), error);
}
network_inaddr_any_server()只是调用了socket_inaddr_any_server()来创建socket,socket_inaddr_any_server()函数的具体实现在/system/core/libcutils/
下,如下所示:
/system/core/libcutils/socket_inaddr_any_server_unix.cpp
int socket_inaddr_any_server(int port, int type)
{
struct sockaddr_in6 addr;
int s, n;
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(port);
addr.sin6_addr = in6addr_any;
s = socket(AF_INET6, type, 0);
if (s < 0) return -1;
n = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *) &n, sizeof(n));
if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
close(s);
return -1;
}
if (type == SOCK_STREAM) {
int ret;
ret = listen(s, LISTEN_BACKLOG);
if (ret < 0) {
close(s);
return -1;
}
}
return s;
}
3.2 adb_socket_accept
在过去server端的socket后,我们就会调用adb_socket_accept()函数来接受请求;其实内部实现也还是调用accept()方法,如下所示:
static __inline__ int adb_socket_accept(int serverfd, struct sockaddr* addr, socklen_t *addrlen)
{
int fd;
fd = TEMP_FAILURE_RETRY( accept( serverfd, addr, addrlen ) );
if (fd >= 0)
close_on_exec(fd);
return fd;
}
但accept()成功接受到数据,就会返回一个新的fd,这个fd就代表这这个通信连接,我们就可以在对这个fd进行write()/send()操作来发送传输数据,和read()/reve()操作来接收传输数据.
最后,我们会将代表的通信连接的fd传入到register_socket_transport()函数中进行tcp通信事件的注册.
4.register_socket_transport()
int register_socket_transport(int s, const char* serial, int port, int local) {
atransport* t = new atransport();
...
D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
if (init_socket_transport(t, s, port, local) < 0) {
delete t;
return -1;
}
...
pending_list.push_front(t);
t->serial = strdup(serial);
...
register_transport(t);
return 0;
}
register_socket_transport()函数中,
- 首先获取到atransport实例,然后调用init_socket_transport()函数来给这个atransport赋值
- 然后将当前赋值完成后的atransport添加到pending_list列表中.
- 最后调用register_transport()函数进行对当前的atransport实例的注册,
接下来我们到register_transport()的实现中看看:
5. register_transport()
/* the fdevent select pump is single threaded */
static void register_transport(atransport* transport) {
tmsg m;
m.transport = transport;
m.action = 1;
D("transport: %s registered", transport->serial);
if (transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
在register_transport,将传进来的transport封装成tmsg,然后通过transport_write_action()函数写入到transport_registration_send中,这个transport_registration_send我们多次遇到,这前面的usb的通信的梳理中已经说明,之后的处理情况就和usb类似,这里就不在赘述.
接下来我们就去看看host算的TCP初始化是怎么样子的.
host端的TCP连接初始化和传输处理流程
host端在tcp通信中充当的就是client,client并不需要早早初始化好.一般情况需要等到server端准备就绪后,client才会去连接server并进行通信,
一般情况下,host端的tcp连接不存在的.需要我们手动键入"adb connect ip:port"来手动连接tcp.并且在连接tcp之前会手动设置端口.所以,在host连接TCP是需要先知道目标server的IP_address和端口,才能去连接server端的,我们现在就去看看源码中是怎么实现的.
说明
在终端键入"adb connect ip_address:port"后,首先会有一段adb client和 adb server通信的过程,然后adb server 会对adb client 的请求做出相应的处理,并返回结果在终端上;这里的adb client可以理解为终端,adb server才是实际处理adb命令的真正主体.这里的tcp连接可以理解为是host端的adb server和Android设备端的adbd连接,adb server是tcp通信中的client, adbd 是server
我们就从adb server 连接TCP是开始看起:
1. host_service_to_socket
host_service_to_socket()函数会帮助adb server处理adb client的请求,我们看当我们此时的请求为"adb connect ip_address:port"时,代码运行的状态:
asocket* host_service_to_socket(const char* name, const char* serial, TransportId transport_id) {
if (!strcmp(name, "track-devices")) {
return create_device_tracker(false);
...
} else if (!strncmp(name, "connect:", 8)) {
char* host = strdup(name + 8);
int fd = create_service_thread("connect", connect_service, host);
if (fd == -1) {
free(host);
}
return create_local_socket(fd);
}
return NULL;
}
我们就根根据上面的代码来看看,首先host_service_to_socket()函数会根据adb client的请求名称来匹配.如果当前的请求就是需要connect
Android设备时:
- 首先会调用create_service_thread()函数将
name
【请求名称】;connect_device
【处理函数】;host
【连接目标】传入. - 然后调用create_local_socket()将上面create_service_thread()函数返回的fd传入,用来后续处理和adbd的连接
1.1 create_service_thread
static int create_service_thread(const char* service_name, void (*func)(int, void*), void* cookie) {
int s[2];
if (adb_socketpair(s)) {
printf("cannot create service socket pair\n");
return -1;
}
...
stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
if (sti == nullptr) {
fatal("cannot allocate stinfo");
}
sti->service_name = service_name;
sti->func = func;
sti->cookie = cookie;
sti->fd = s[1];
std::thread(service_bootstrap_func, sti).detach();
return s[0];
}
create_service_thread()内部主要是创建stinfo,并进行赋值,然后创建一个新的现成调用service_bootstrap_func()
1.1.1 service_bootstrap_func
static void service_bootstrap_func(void* x) {
stinfo* sti = reinterpret_cast<stinfo*>(x);
adb_thread_setname(android::base::StringPrintf("%s svc %d", sti->service_name, sti->fd));
sti->func(sti->fd, sti->cookie);
free(sti);
}
service_bootstrap_func()函数其实也没有做什么其他的,只是执行了一个函数,就是在内部调用了封装在stinfo中的方法connect_service(),
接着进入到connect_service()函数中看看:
2 connect_service
static void connect_service(int fd, void* data) {
...
if (!strncmp(host, "emu:", 4)) {
connect_emulator(host + 4, &response);
} else {
connect_device(host, &response);
}
...
adb_close(fd);
}
进入到connect_service中,会有一个判断,就是判断请求连接的目标是emulator还是Android`s device ,由于我们是要连接Android 设备,所以会走到函数中connect_device()中;
2.1 connect_device
void connect_device(const std::string& address, std::string* response) {
...
std::string serial;
std::string host;
int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
if (!android::base::ParseNetAddress(address, &host, &port, &serial, response)) {
return;
}
std::string error;
int fd = network_connect(host.c_str(), port, SOCK_STREAM, 10, &error);
...
// Send a TCP keepalive ping to the device every second so we can detect disconnects.
if (!set_tcp_keepalive(fd, 1)) {
D("warning: failed to configure TCP keepalives (%s)", strerror(errno));
}
int ret = register_socket_transport(fd, serial.c_str(), port, 0);
...
}
这个connect_device()函数主要就是处理TCP 中创建socket,然后注册一个socket传输事件;
2.1.1 network_connect
- 首先调用network_connect()去了创建socket去连接目标地址,
inline int network_connect(const std::string& host, int port, int type,
int timeout, std::string* error) {
int getaddrinfo_error = 0;
int fd = socket_network_client_timeout(host.c_str(), port, type, timeout,
&getaddrinfo_error);
...
return fd;
...
}
network_connect()函数是调用socket_network_client_timeout()函数的实现在/system/core/libcutils/socket_network_client_unix.cpp
中,这主要就是对socket编程中的一些操作,然后直接表示连接的fd,我们可以通过这个fd进行write()/send()操作进行数据发送任务,或者进行read()/reve()操作进行数据接受的任务.
-然后,调用register_socket_transport()函数对当前的socket连接注册一个socket传输事件;
2.1.2 register_socket_transport
int register_socket_transport(int s, const char* serial, int port, int local) {
atransport* t = new atransport();
...
D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
if (init_socket_transport(t, s, port, local) < 0) {
...
pending_list.push_front(t);
t->serial = strdup(serial);
...
register_transport(t);
return 0;
}
这个函数已经多次讲过.就是创建atransport实例,然后赋值初始化,将当前atransport实例装进init_socket_transport中,最后调用register_transport()注册一次传输事件,不论是usb,还是tcp传输,都是调用这个函数进行的:
2.1.2.1 register_transport
static void register_transport(atransport* transport) {
tmsg m;
m.transport = transport;
m.action = 1;
D("transport: %s registered", transport->serial);
if (transport_write_action(transport_registration_send, &m)) {
fatal_errno("cannot write transport registration socket\n");
}
}
register_transport()函数的主要逻辑就是:将传进来的transport封装成tmsg,然后通过transport_write_action()函数写入到transport_registration_send中,这个transport_registration_send我们多次遇到,这前面的usb的通信的梳理中已经说明,之后的处理情况就和usb类似,这里也不在赘述.