前言
前文
ADB(一)_概况了解
ADB(二)_ADBD_main()函数代码梳理
前面我们对ABD的adbd部分的main()方法进行大概梳理,了解到main()函数的结构和函数调用;今天我们了解adbd_main()函数是怎么工作的;
1. adb_main 代码说明
int adbd_main(int server_port) {
//将当前进程的文件创建掩码设置为mask,并返回旧的创建掩码。
umask(0);
// 为了避免进程退出, 可以捕获SIGPIPE信号, 给它设置SIG_IGN信号处理函数:
signal(SIGPIPE, SIG_IGN);
// 初始化传输注册socketpair
init_transport_registration();
// We need to call this even if auth isn't enabled because the file
// descriptor will always be open.
adbd_cloexec_auth_socket();
if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
auth_required = false;
}
//adbd授权初始化
adbd_auth_init();
// Our external storage path may be different than apps, since
// we aren't able to bind mount after dropping root.
const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
if (adb_external_storage != nullptr) {
setenv("EXTERNAL_STORAGE", adb_external_storage, 1);
} else {
D("Warning: ADB_EXTERNAL_STORAGE is not set. Leaving EXTERNAL_STORAGE"
" unchanged.\n");
}
// 降低特权
drop_privileges(server_port);
bool is_usb = false;
if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
}
// If one of these properties is set, also listen on that port.
// If one of the properties isn't set and we couldn't listen on usb, listen
// on the default 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) {
D("using port=%d", port);
// Listen on TCP port specified by service.adb.tcp.port property.
setup_port(port);
} else if (!is_usb) {
// Listen on default port.
setup_port(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
}
D("adbd_main(): pre init_jdwp()");
init_jdwp();
D("adbd_main(): post init_jdwp()");
D("Event loop starting");
fdevent_loop();
return 0;
}
adbd_main()函数调用时序图如下所示:
1.1 umask
umask函数是系统提供的函数。将当前进程的文件创建掩码设置为mask,并返回旧的创建掩码;[权限掩码]是由3个八进制的数字所组成。这是linux系统常见的为文件权限知识,此处就不赘述了。
1.2 signal
signal信号函数,第一个参数表示需要处理的信号值(SIGPIPE),第二个参数为处理函数或者是一个表示,这里,SIG_IGN表示忽略SIGPIPE信号。
- 问: 那为什么会出现SIGPIPE信号呢?
- 答:这里可以参考一下别人的总结:signal(SIGCHLD, SIG_IGN)和signal(SIGPIPE, SIG_IGN);
1.3 init_transport_registration
init_transport_registration()函数主要就是初始化adbd的传输连接。函数内部代码如下所示:
void init_transport_registration(void) {
int s[2];
if (adb_socketpair(s)) {
fatal_errno("cannot open transport registration socketpair");
}
D("socketpair: (%d,%d)", s[0], s[1]);
transport_registration_send = s[0];
transport_registration_recv = s[1];
fdevent_install(&transport_registration_fde, transport_registration_recv,
transport_registration_func, 0);
fdevent_set(&transport_registration_fde, FDE_READ);
}
如上所示,init_transport_registration()函数首先调用adb_sockpair()函数创建两个socket【套接字】,并将这两个套接字相互连接。 这里我们关注两个函数,
static __inline__ int unix_socketpair( int d, int type, int protocol, int sv[2] )
{
return socketpair( d, type, protocol, sv );
}
static __inline__ int adb_socketpair( int sv[2] )
{
int rc;
rc = unix_socketpair( AF_UNIX, SOCK_STREAM, 0, sv );
if (rc < 0)
return -1;
close_on_exec( sv[0] );
close_on_exec( sv[1] );
return 0;
}
1.3.1 adb_socketpair()
1.3.1.1 socketpair()
adb_socketpair()最终调用的是socketpair()函数,socketpair()函数会创建一对套接字,并且将这对套接字连接相互通信;这对套接字可以用于全双工通信,每一个套接字既可以读也可以写;就是一个管道。例如,可以往s[0]中写,从s[1]中读;或者从s[1]中写,从s[0]中读;
1.3.1.2 close_on_exec()
在生成一对相互通信的套接字后,调用了close_on_exec()函数;这个函数内部调用了fcntl()函数:
static __inline__ void close_on_exec(int fd)
{
fcntl( fd, F_SETFD, FD_CLOEXEC );
}
这是一个安全操作文件的一个知识点,特别留意一下:
- close_on_exec是一个进程所有文件描述符的标记位图,每个比特位代表一个打开的文件描述符,用于确定在调用系统调用execve()时需要关闭的文件句柄 (参见include/fcntl.h)。当一个程序使用fork()函数创建了一个子进程时,往往会在该子进程中调用execve()函数加载执行另一个新程序,此时子进程将完全被新程序替换掉,并在子进程中开始执行新程序。同时子进程会拷贝父进程的文件描述符表,这样父子进程就有可能同时操作同一打开文件,如果不想子进程操作该文件描述符,则可将close_on_exec中的对应比特位被设置为1,那么在执行execve()时该描述符将被关闭,否则该描述符将始终处于打开状态。当打开一个文件时,默认情况下文件句柄在子进程中也处于打开状态。设置相应标志位则需要fcntl系统调用。
这里的具体原因可以看看别人总结的博客:Linux-close_on_exec标志位https://www.cnblogs.com/ptfe/p/11060551.html
1.3.2 fdevent_install
在获取到两个相互通信的套接字后分别将他们复制给transport_registration_send和transport_registration_recv变量。然后调用fdevent_install()函数,如下所示:
fdevent_install(&transport_registration_fde, transport_registration_recv,
transport_registration_func, 0);
fdevent_install()函数的实现为:
void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
check_main_thread();
CHECK_GE(fd, 0);
memset(fde, 0, sizeof(fdevent));
fde->state = FDE_ACTIVE;
fde->fd = fd;
fde->func = func;
fde->arg = arg;
if (!set_file_block_mode(fd, false)) {
// Here is not proper to handle the error. If it fails here, some error is
// likely to be detected by poll(), then we can let the callback function
// to handle it.
LOG(ERROR) << "failed to set non-blocking mode for fd " << fd;
}
auto pair = g_poll_node_map.emplace(fde->fd, PollNode(fde));
CHECK(pair.second) << "install existing fd " << fd;
D("fdevent_install %s", dump_fde(fde).c_str());
}
1.3.2.1 fdevent
我们首先关注一下fdevent_install()的三个参数,第一个参数为transport_registration_fde
,transport_registration_fde 是结构struct fdevent
,我们来看看它的定义:
struct fdevent {
fdevent *next;
fdevent *prev;
int fd;
int force_eof;
uint16_t state;
uint16_t events;
fd_func func;
void *arg;
};
fdevent就是将事件和处理函数封装成一个结构体,然后在接下来的代码中,会对事件进行监听,并调用相应的处理函数进行处理。
1.3.2.2 transport_registration_func
在adb_socketpair 函数建立了一个管道,并且把管道的一头放入fdevent中进行读监听,当有数据可读时候调用transport_registration_func 函数,transport_registration_func就是用来处理transport_registration_recv接收到数据时进行处理。
再在回到fdevent_install()中,说先需要检测是否在主线程中,这是因为所有对fdevent的操作都必须要在主线程中进行,
- 确认是在主线程后们就开始为fdevent分配内存,
- 将传进来的transport_registration_func装进fdevent中。
- 然后将fdevent封装成PollNode节点
- 将新建的PoolNode节点装进g_poll_node_map中。
PollNode节点和g_poll_node_map的定义如下:
/system/core/adb/fdevent.cpp
struct PollNode {
fdevent* fde;
adb_pollfd pollfd;
explicit PollNode(fdevent* fde) : fde(fde) {
memset(&pollfd, 0, sizeof(pollfd));
pollfd.fd = fde->fd;
#if defined(__linux__)
// Always enable POLLRDHUP, so the host server can take action when some clients disconnect.
// Then we can avoid leaving many sockets in CLOSE_WAIT state. See http://b/23314034.
pollfd.events = POLLRDHUP;
#endif
}
};
...
static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
注:这个g_poll_node_map我们要注意,后面会看它的用处。
1.3.3 fdevent_set()
最后,在init_transport_registration()函数中会调用fdevent_set()函数:
void fdevent_set(fdevent* fde, unsigned events) {
check_main_thread();
events &= FDE_EVENTMASK;
if ((fde->state & FDE_EVENTMASK) == events) {
return;
}
CHECK(fde->state & FDE_ACTIVE);
fdevent_update(fde, events);
D("fdevent_set: %s, events = %u", dump_fde(fde).c_str(), events);
if (fde->state & FDE_PENDING) {
// If we are pending, make sure we don't signal an event that is no longer wanted.
fde->events &= events;
if (fde->events == 0) {
g_pending_list.remove(fde);
fde->state &= ~FDE_PENDING;
}
}
}
同样,只要涉及到fdevent的操作必须要在主线程中,所以这里先要检验是不是在主线程中,然后只要做的工作就是更新是检验码和调用fdevent_update()函数来更新fdevent的状态。
1.4 adbd_cloexec_auth_socket
adbd_cloexec_auth_socket这个函数就比较简单了,我们可以在调用它的注释中看到;主要是为了保证文件操作安全
// We need to call this even if auth isn't enabled because the file
// descriptor will always be open.
adbd_cloexec_auth_socket();
adbd_cloexec_auth_socket()的实现如下:
void adbd_cloexec_auth_socket() {
int fd = android_get_control_socket("adbd");
if (fd == -1) {
PLOG(ERROR) << "Failed to get adbd socket";
return;
}
fcntl(fd, F_SETFD, FD_CLOEXEC);
}
adbd_cloexec_auth_socket()函数的实现就是先拿到adbd的套接字,然后调用fcntl()函数标记为FD_CLOEXEC,这个和前面讨论的1.3.1.2 close_on_exec()函数功能是一样的;
1.5 adbd_auth_init
紧接着,通过获取Android系统的设置,给auth_required赋值;然后调用adbd_auth_init()函数进行adbd授权初始化。
if (ALLOW_ADBD_NO_AUTH && !android::base::GetBoolProperty("ro.adb.secure", false)) {
auth_required = false;
}
adbd_auth_init();
adbd_auth_init()函数负责完成对连入的PC端身份验证功能的初始化工作的, 实现如下:
void adbd_auth_init(void) {
int fd = android_get_control_socket("adbd");
if (fd == -1) {
PLOG(ERROR) << "Failed to get adbd socket";
return;
}
if (listen(fd, 4) == -1) {
PLOG(ERROR) << "Failed to listen on '" << fd << "'";
return;
}
fdevent_install(&listener_fde, fd, adbd_auth_listener, NULL);
fdevent_add(&listener_fde, FDE_READ);
}
adbd_auth_init()函数的实现原理和1.3 init_transport_registration()函数一样,这里就不在赘述,不清楚的话再将init_transport_registration()函数的实现再回顾回顾。
1.6 drop_privileges
这个drop_privilege()看函数名,就只可以判断可以降低权限。所以这个函数主要就是对调用者权限从root权限到shell权限的控制,函数内部的逻辑比较简单:主要是通过调动should_drop_privileges()函数来判断是否应该降低权限,在进行相应的操作
static void drop_privileges(int server_port) {
ScopedMinijail jail(minijail_new());
...
if (should_drop_privileges()) { ...
//第一步,更改群组为AID_SHELL,降低为shell权限
//第二步,清除clearing the inheritable, effective, and permitted sets.
...
} else {
//否则就是root权限,监听默认端口,
if(install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)){
...
}
...
}
1.6.1 minijail
在drop_privileges()函数中,对于权限的控制使用了minijail,这是一个沙盒相关技术,使得软件运行在操作系统受限制的环境中。这里我们在没有root权限时,就会把权限降到Shell权限,只允许shell进程访问。在drop_privileges()函数中还为我们还提供了一些与ADB 相关的其他的UID:
// Add extra groups:
// AID_ADB to access the USB driver
// AID_LOG to read system logs (adb logcat)
// AID_INPUT to diagnose input issues (getevent)
// AID_INET to diagnose network issues (ping)
// AID_NET_BT and AID_NET_BT_ADMIN to diagnose bluetooth (hcidump)
// AID_SDCARD_R to allow reading from the SD card
// AID_SDCARD_RW to allow writing to the SD card
// AID_NET_BW_STATS to read out qtaguid statistics
// AID_READPROC for reading /proc entries across UID boundaries
// AID_UHID for using 'hid' command to read/write to /dev/uhid
gid_t groups[] = {AID_ADB, AID_LOG, AID_INPUT, AID_INET,
AID_NET_BT, AID_NET_BT_ADMIN, AID_SDCARD_R, AID_SDCARD_RW,
AID_NET_BW_STATS, AID_READPROC, AID_UHID};
1.6.2 install_listener
在drop_privileges()函数中。当权限为root时,就会调用install_listener()函数注册一个监听来监听默认端口5037。
install_listener()内部执行大致流程如下:
InstallStatus install_listener(const std::string& local_name, const char* connect_to,
atransport* transport, int no_rebind, int* resolved_tcp_port,
std::string* error) EXCLUDES(listener_list_mutex) {
std::lock_guard<std::mutex> lock(listener_list_mutex);
for (auto& l : listener_list) {
if (local_name == l->local_name) {
...
return INSTALL_STATUS_OK;
}
}
auto listener = std::make_unique<alistener>(local_name, connect_to);
int resolved = 0;
listener->fd = socket_spec_listen(listener->local_name, error, &resolved);
...
close_on_exec(listener->fd);
if (listener->connect_to == "*smartsocket*") {
fdevent_install(&listener->fde, listener->fd, ss_listener_event_func, listener.get());
} else {
fdevent_install(&listener->fde, listener->fd, listener_event_func, listener.get());
}
fdevent_set(&listener->fde, FDE_READ);
...
listener_list.push_back(std::move(listener));
return INSTALL_STATUS_OK;
}
1.6.2 .1 EXCLUDES
我们在查看install_listener()函数是,返现有个属性字EXCLUDES,那么这个EXCLUDES是个什么呢?
可以参考这篇博客:C++_GUARDED_BY 和EXCLUDES属性字
1.6.2 .2 listener_list
listener_list是存放alistener的一个list,它的定义如下:system/core/adb/adb_listeners.cpp
,
// listener_list retains ownership of all created alistener objects. Removing an alistener from
// this list will cause it to be deleted.
static auto& listener_list_mutex = *new std::mutex();
typedef std::list<std::unique_ptr<alistener>> ListenerList;
static ListenerList& listener_list GUARDED_BY(listener_list_mutex) = *new ListenerList();
这个alistener又是alistener类的一个实例,alistener累的定义如下:
class alistener {
public:
alistener(const std::string& _local_name, const std::string& _connect_to);
~alistener();
fdevent fde;
int fd = -1;
std::string local_name;
std::string connect_to;
atransport* transport = nullptr;
adisconnect disconnect;
private:
DISALLOW_COPY_AND_ASSIGN(alistener);
};
alistener是绑定到本地端口的实体,在接收到该端口上的连接时,创建一个asocket以将新的本地连接连接到特定的远程服务。
install_listener()函数会先判断是否已经和默认端口绑定连接,如果是的就返回INSTALL_STATUS_OK;否者就会新建一个套接字,然后调用fdevent_install()函数处理套接字和回调函数,具体实现过程可参考前面1.3.2 fdevent_install;1.3.3 fdevent_set的分析。
在上述连接工作完成后,就会将新的alistener装进listener_list中,然后返回INSTALL_STATUS_OK。
1.7 usb_init
在权限处理完成后,就会调用usb_init()函数来处理usb连接的问题,
bool is_usb = false;
if (access(USB_FFS_ADB_EP0, F_OK) == 0) {
// Listen on USB.
usb_init();
is_usb = true;
}
首先通过access()函数判断USB设备时候存在,如果存在就会进入usb_init()函数。
void usb_init() {
dummy_fd = adb_open("/dev/null", O_WRONLY);
CHECK_NE(dummy_fd, -1);
usb_ffs_init();
}
usb_init()函数首先open,文件"/dev/null"
;然后调用usb_ffs_inint()函数;usb_ffs_inint()函数实现如下:
static void usb_ffs_init() {
D("[ usb_init - using FunctionFS ]");
usb_handle* h = new usb_handle();
if (android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false)) {
// Devices on older kernels (< 3.18) will not have aio support for ffs
// unless backported. Fall back on the non-aio functions instead.
h->write = usb_ffs_write;
h->read = usb_ffs_read;
} else {
h->write = usb_ffs_aio_write;
h->read = usb_ffs_aio_read;
aio_block_init(&h->read_aiob);
aio_block_init(&h->write_aiob);
}
h->kick = usb_ffs_kick;
h->close = usb_ffs_close;
D("[ usb_init - starting thread ]");
std::thread(usb_ffs_open_thread, h).detach();
}
usb_ffs_init()函数首先获取usb_handle
结构的一个实例,将usb的读写函数封装进usb_handle中,然后新起一个线程专门用来监听处理usb通信。
1.8 setup_port
usb监听完成监听后,就会根据android系统属性,当"service.adb.tcp.port"
开启时,并且通过 "persist.adb.tcp.port"
属性指定端口号,就会调用setup_port()函数来处理TCP连接,如果port没有指定,则使用默认端口。
setup_port()函数是实现为:
static void setup_port(int port) {
local_init(port);
setup_mdns(port);
}
setup_port()函数内部首先调用local_init()函数,local_init()函数实现为:
1.8.1 local_init
void local_init(int port)
{
void (*func)(int);
const char* debug_name = "";
// For the adbd daemon in the system image we need to distinguish
// between the device, and the emulator.
func = use_qemu_goldfish() ? qemu_socket_thread : server_socket_thread;
debug_name = "server";
D("transport: local %s init", debug_name);
std::thread(func, port).detach();
}
use_qemu_goldfish()通过android的系统属性来判断使用qemu_socket_thread还是server_socket_thread来处理TCP连接。最后也会起一个新的线程来专门处理TCP连接。
1.8.2 setup_mdns
- mDNS , multicast DNS, 可以理解为局域网内部的 DNS 系统,它和 DNS 有很多相似的地方,通过它可以实现局域网内部的服务发现、查找和广播。同时它是基于组播的协议。
- mDNS : multicast DNS ,规范文档地址: http://www.ietf.org/rfc/rfc6762.txt。
setup_mdns的实现为:
void setup_mdns(int port_in) {
port = port_in;
std::thread(setup_mdns_thread).detach();
// TODO: Make this more robust against a hard kill.
atexit(teardown_mdns);
}
可以见到,setup_mdns也是另起一个线程处理mdns服务;
1.9 init_jdwp
我们先了解一些什么是jdwp:
- JDWP 是 Java Debug Wire Protocol 的缩写,它定义了调试器(debugger)和被调试的 Java 虚拟机(target vm)之间的通信协议。
init_jdwp的内部调用了jwdp_control_init();接下来看看是怎么实现的
int init_jdwp(void) {
return jdwp_control_init(&_jdwp_control, JDWP_CONTROL_NAME, JDWP_CONTROL_NAME_LEN);
}
static int jdwp_control_init(JdwpControl* control, const char* sockname, int socknamelen) {
sockaddr_un addr;
socklen_t addrlen;
int s;
...
s = socket(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0);
...
control->listen_socket = s;
control->fde = fdevent_create(s, jdwp_control_event, control);
...
/* only wait for incoming connections */
fdevent_add(control->fde, FDE_READ);
...
return 0;
}
jwdp_control_init()首先创建套接字,绑定套接字,将jdwp_control_event()函数封装和添加到fdevent中。fdevent我们在文章前面讨论过,这里就不细说了;
1.10 fdevent_loop
看函数的名字,就可以才想到,最后可能会进入一个loop循环中,在loop循环中监听fdevent,对发生的event进行处理,那我们接下来就要验证一下fdevent_loop()函数的内部实现是不是和我们的猜想一样呢?
void fdevent_loop() {
set_main_thread();
fdevent_run_setup();
while (true) {
if (terminate_loop) {
return;
}
D("--- --- waiting for events");
fdevent_process();
while (!g_pending_list.empty()) {
fdevent* fde = g_pending_list.front();
g_pending_list.pop_front();
fdevent_call_fdfunc(fde);
}
}
}
果然,在fdevent_loop()中就是进入一个死循环先监听fdevent并更新fdevent,主要在fdevent_process()函数中将g_poll_node_map中的fdevent转移到 std::vector<adb_pollfd> pollfds
,然后利用liunx的poll()来论循,接着就是在fdevent_call_fdfunc()函数里分别调用事件相关的处理函数处理。
到此我们终于将adbd的启动流程梳理完了
接下来我们会进行ADB在host端代码的梳理:
ADB(四)_host端的代码梳理