Anbox 通过一个可执行文件,实现多个不同的应该用逻辑。在启动 Anbox 可执行文件时,通过为它提供不同的命令行参数来确定具体执行哪个命令。Anbox 中这些不同的命令实例之间,整体的通信架构如下图这样:
这些不同的命令实例之间通信的过程大体如下:
- 容器管理器实例首先运行起来,监听在特定位置的 Unix 域 Socket 上;
- 随后会话管理器启动,监听在另外的一些 Unix 域 Socket 上;
- 会话管理器同时连接容器管理器监听的 Unix 域 Socket 上的服务;
- 会话管理器与容器管理器通过 Unix 域 Socket 成功建立连接之后,会话管理器向容器管理器发送命令,请求容器管理器启动 Android 容器;
- 容器管理器收到会话管理器发来的命令后,先给会话管理器一个响应,然后通过 LXC 启动一个 Android 容器,并将会话管理器监听的 Unix 域 Socket 的文件路径映射进 Android 容器的
/dev/目录下; - Android 容器启动之后,容器内的 Android 进程,通过映射进来的 Unix 域 Socket 与会话管理器建立连接;
- Android 容器启动时,会话管理器与 ADB 守护进程建立连接;
- Anbox 的 install 和 launch 命令主要用于对 Android 容器做一些控制,它们分别用于向 Android 容器中安装应用程序 APK 以及启动容器内的特定 Activity,它们通过 D-Bus 与会话管理器通信。
在 Anbox 中,会话管理器和容器管理器之间是比较重要的一条通信通道。会话管理器和容器管理器之间通过 Unix 域 Socket 进行通信,容器管理器监听在特定位置的 Unix 域 Socket 上,会话管理器发起与容器管理器之间的连接,连接建立之后,两者通过这条连接进行通信。
容器管理器接受 RPC 调用
代码层面,在容器管理器一端,通过 anbox::container::Service 启动对 Unix 域 Socket 的监听。anbox::container::Service 的定义(位于anbox/src/anbox/container/service.h)如下:
dispatcher_ 看起来没有实际的用处。 anbox::container::Service 的实现(位于 anbox/src/anbox/container/service.cpp)如下:
在 anbox::container::Service 的构造函数中,通过 anbox::network::PublishedSocketConnector 及 anbox::network::DelegateConnectionCreator 等组件,启动对 Unix 域 Socket 的监听。Anbox 中处理 Unix 域 Socket 监听的基本方法/模型,请参考 Anbox 实现分析 2:I/O 模型 一文中的相关部分。
anbox::container::Service 通过 anbox::network::Connections 和
anbox::network::SocketConnection 等管理新接受的连接,它限制只与一个会话管理器实例建立一条连接。anbox::container::Service 将处理收到的消息的组件 anbox::container::ManagementApiMessageProcessor 与底层的连接粘起来。
Anbox 的容器管理器和会话管理器通过基于 Protobuf 设计的 RPC 进行通信。anbox::container::Service 中处理收到的消息及接受 RPC 调用的相关组件的设计框架如下:

在 Anbox 的设计中,anbox::rpc::Channel 及 anbox::rpc::PendingCallCache 本来主要用于 RPC 调用发起端的消息收发,但在 anbox::container::Service::new_client() 中,同样为新连接创建了这两个类的对象,这显得有点多次一举。
anbox::container::Service 通过 anbox::network::SocketConnection 收到消息之后,首先交给 anbox::rpc::MessageProcessor 的 process_data() 处理。
anbox::rpc::MessageProcessor 的定义(位于 anbox/src/anbox/rpc/message_processor.h)如下:
anbox::rpc::MessageProcessor 的实现(位于 anbox/src/anbox/rpc/message_processor.cpp)如下:
在会话管理器与容器管理器之间的 RPC 通信中,anbox::rpc::MessageProcessor 是一个同时用于 RPC 调用发起端和接受端的组件。容器管理器作为 RPC 调用的接受端,接收发自于会话管理器的类型为 MessageType::invocation 的消息。
会话管理器与容器管理器之间的 RPC 通信的消息格式为:[3 个字节的消息头] + [经由 Protobuf MessageLite 对象序列化得到的消息体],其中消息头的前两个字节为 16 位的消息体长度的大尾端表示,第 3 个字节为消息的类型。RPC 消息的具体定义在 anbox/src/anbox/protobuf/anbox_rpc.proto 文件中:
Invocation 消息用于发起 RPC 调用,Result、Void 和 StructuredError 用于返回响应或错误消息。
对于容器管理器而言,anbox::rpc::MessageProcessor 在其 process_data() 中首先提取消息头,得到消息体的长度和类型,然后提取消息体并反序列化得到 Protobuf 消息 anbox::protobuf::rpc::Invocation,随后将该 Protobuf 消息封装为 anbox::rpc::Invocation 类的对象,并调用 dispatch(Invocation const&) 将消息派发出去。
anbox::rpc::Invocation 类的定义(位于 anbox/src/anbox/rpc/message_processor.h 中)如下:
anbox::rpc::Invocation 类的实现(位于 anbox/src/anbox/rpc/message_processor.cpp 中)如下:
anbox::rpc::Invocation 类只是对 anbox::protobuf::rpc::Invocation 的简单包装。
anbox::rpc::MessageProcessor 的 dispatch(Invocation const&) 是一个虚函数,其实际的实现位于 ManagementApiMessageProcessor 中。anbox::container::ManagementApiMessageProcessor 的定义(位于 anbox/src/anbox/container/management_api_message_processor.h 中)如下:
anbox::container::ManagementApiMessageProcessor 的实现(位于 anbox/src/anbox/container/management_api_message_processor.cpp 中)如下:
anbox::container::ManagementApiMessageProcessor 的实现很简单,只支持两种 RPC 调用,分别为启动 Android 容器和停止 Android 容器,在它的 dispatch() 函数中,根据方法名,调用对应的函数。
函数调用通过一个函数模板 invoke() 完成,该函数模板定义(位于 anbox/src/anbox/rpc/template_message_processor.h)如下:
直接启动和停止 Android 容器的职责,由 anbox::container::ManagementApiSkeleton 完成,这个类的定义(位于 anbox/src/anbox/container/management_api_skeleton.h)如下:
这个类的定义很简单,其实现(位于 anbox/src/anbox/container/management_api_skeleton.cpp)如下:
anbox::container::ManagementApiSkeleton 通过 Container 类启动或停止 Android 容器。配合函数模板 invoke() 的定义,及 Protobuf 的相关方法实现,不难理解, start_container() 和 stop_container() 函数的参数消息,在 invoke() 函数中由 Invocation 消息的参数字段的字节数组反序列化得到,这两个函数的执行过程,都是向 response 参数中填入返回给调用者的响应,并通过 done->Run() 将响应通过 ManagementApiMessageProcessor::send_response() 函数,即
anbox::rpc::MessageProcessor::send_response() 函数发送回调用端。
在 anbox::rpc::MessageProcessor::send_response() 函数中,先将响应序列化,然后将序列化之后的响应放进 anbox::protobuf::rpc::Result Protobuf 消息中,最后再将 anbox::protobuf::rpc::Result 包装为 Anbox 的 RPC 消息发送回调用端。
anbox::container::ManagementApiSkeleton 的 pending_calls_ 似乎也没有实际的用处。
至此整个 RPC 调用接受处理流程结束。整个流程如下图所示:

会话管理器发起 RPC 调用
在 Anbox 的会话管理器中,通过 anbox::container::Client 发起与容器管理器之间的连接,并处理双方之间的 RPC 通信,这个类的定义(位于 anbox/src/anbox/container/client.h)如下:
anbox::container::Client 主要向外部暴露了两个接口,一是启动容器,二是停止容器,SessionManager 通过这两个接口来控制容器的启动与停止。anbox::container::Client 类的实现(位于 anbox/src/anbox/container/client.cpp)如下:
anbox::container::Client 类在其构造函数中,即通过 Unix 域 Socket 建立了与容器管理器的连接,它通过 ManagementApiStub 发起 RPC 调用。ManagementApiStub 是容器管理器与会话管理器间 RPC 进程间通信在 RPC 调用发起端的接口层,它提供了 启动 Android 容器 及 关闭 Android 容器 这样的抽象。在 ManagementApiStub 之下,是容器管理器与会话管理器间 RPC 进程间通信的 RPC 层,即 anbox::rpc::Channel,主要用于处理消息的发送。
anbox::container::Client 类本身处理连接中原始数据的接收,这里直接用了裸 SocketMessenger,而没有再用 SocketConnection 封装。anbox::container::Client 收到数据之后,会将数据丢给 anbox::rpc::MessageProcessor 处理。类型为anbox::rpc::PendingCallCache 的 pending_calls_ 主要用于处理 RPC 的异步调用。在 anbox::rpc::Channel 中,消息发送之后,并不会等待响应的接收,而是在 pending_calls_ 中为 RPC 调用注册一个完成回调。在 anbox::rpc::MessageProcessor 中收到响应的消息之后,前面的完成回调被调用,RPC 调用的发起者得到通知。
anbox::container::Client 中处理 RPC 调用的发起的相关组件的设计框架如下:

anbox::container::Client 直接使用 anbox::container::ManagementApiStub 执行 RPC 调用,这个类的定义(位于 anbox/src/anbox/container/management_api_stub.h)如下:
anbox::container::ManagementApiStub 定义了启动容器和停止容器的接口,并定义了容器启动完成和容器停止完成之后的回调,它还定义了 Request 类,用于封装请求的响应,及一个 WaitHandle。WaitHandle 由 RPC 调用的发起端用于等待请求的结束。
anbox::container::ManagementApiStub 类的实现(位于 anbox/src/anbox/container/management_api_stub.cpp)如下:
尽管实际的 RPC 调用是异步的,但 anbox::container::ManagementApiStub 类通过条件变量为其调用者提供了一种同步执行的假象。启动容器和停止容器的行为通过另外的 Protobuf 消息来描述,这些消息的定义(位于 anbox/src/anbox/protobuf/anbox_container.proto)如下:
在 ManagementApiStub::start_container() 和 ManagementApiStub::stop_container() 函数中,将参数封装进对应的 Protobuf 消息中,然后更新 Request 的 WaitHandle 中用于表示期待接收到的响应的状态,随后通过 anbox::rpc::Channel 发起 RPC 调用并注册完成回调,最后等待在 Request 的 WaitHandle 上。
启动容器和停止容器的 RPC 调用完成之后,对应的回调被调用,它们通过相应的请求的 WaitHandle 通知调用结束,ManagementApiStub::start_container() 和 ManagementApiStub::stop_container() 函数返回。
ManagementApiStub 的设计实际上有几处问题。首先是定义的 mutex_ 成员,看上去毫无意义;其次是等待的方法 wait_for_all(),这个函数会一直等待条件成立,如果容器管理器进程意外终止,或者由于其它什么原因,无法给会话管理器发回响应消息,则会话管理器会一直等在那里无法结束,正确的做法应该用有超时的等待,等待一段时间之后,就假设启动容器失败,并退出。
可以看一下 WaitHandle 的设计与实现。这个类的定义(位于 anbox/src/anbox/common/wait_handle.h)如下:
WaitHandle 封装标准库的 std::mutex 和 std::condition_variable 来构造等待设施。这个类的实现(位于 anbox/src/anbox/common/wait_handle.cpp)如下:
需要等待的一端,通过调用 expect_result() 来告诉 WaitHandle,需要等待多接收一个响应,并通过 wait_for_all()、wait_for_pending() 和 wait_for_one() 来等待结果的出现。处理收到的消息的线程,通过 result_received() 通知等待的线程。
anbox::rpc::PendingCallCache 是一个容器,用于保存已经发送了请求消息,已经发起但还没有得到响应的 RPC 调用的描述及完成回调,这个类的定义(位于 anbox/src/anbox/rpc/pending_call_cache.h)如下:
anbox::rpc::PendingCallCache 类还定义一个 PendingCall 用于封装请求的响应对象及完成回调,它用一个 map 保存 PendingCall,由于需要在 anbox::rpc::MessageProcessor::process_data() 和 anbox::rpc::Channel 的线程中访问,为了线程安全计,每次访问都有锁进行保护。
anbox::rpc::PendingCallCache 类的实现(位于 anbox/src/anbox/rpc/pending_call_cache.cpp)如下:
save_completion_details() 用于向 anbox::rpc::PendingCallCache 中放入调用,populate_message_for_result() 用于把返回的响应消息塞给调用,complete_response() 则用于通知结果的返回,调用对应的完成回调。
anbox::rpc::Channel 用于序列化消息,并发送出去,其定义(位于 anbox/src/anbox/rpc/channel.h)如下:
anbox::rpc::Channel 负责为每个调用消息分配 ID。anbox::rpc::Channel 实现(位于 anbox/src/anbox/rpc/channel.cpp)如下:
call_method() 用于发起 RPC 调用,这个函数将 RPC 调用描述及完成回调保存进 pending_calls_ 中,随后发送消息。anbox::rpc::Channel 主要在操作 Protobuf 消息的序列化,此处不再赘述。
可以再看一下 RPC 调用发起端收到响应消息时的处理,主要是 anbox::rpc::MessageProcessor 的下面这一段(位于 anbox/src/anbox/rpc/message_processor.cpp):
这段代码将响应消息塞给 pending_calls_ 中保存的对应的 Invocation,并调用完成回调。
Anbox 中会话管理器与容器管理器通过两个 RPC 调用进行通信,在调用发起端的整个处理过程如下图:


打赏
Done。