ADB(三)_ADBD_adbd_main()函数代码梳理

前言

前文

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()函数调用时序图如下所示:
ADBD_adbd_main函数

1.1 umask

umask函数是系统提供的函数。将当前进程的文件创建掩码设置为mask,并返回旧的创建掩码;[权限掩码]是由3个八进制的数字所组成。这是linux系统常见的为文件权限知识,此处就不赘述了。

1.2 signal

signal信号函数,第一个参数表示需要处理的信号值(SIGPIPE),第二个参数为处理函数或者是一个表示,这里,SIG_IGN表示忽略SIGPIPE信号。

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端的代码梳理

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值