本页面介绍了 Android 8 中 Binder 驱动程序的变更、使用 Binder IPC 的详细信息以及所需的 SELinux 政策。
Binder 驱动程序的变更
从 Android 8 开始,Android 框架和 HAL 现在使用 Binder 相互通信。由于这种通信方式会大幅增加 Binder 流量,因此 Android 8 包含多项旨在保持 Binder IPC 快速的改进。SoC 供应商和 OEM 应直接从 kernel/common 项目的 android-4.4、android-4.9 及更高版本的相关分支合并。
多个 Binder 网域(上下文)
Common-4.4 及更高版本,包括上游为了清晰地划分框架(与设备无关)代码和供应商(与设备相关)代码之间的 Binder 流量,Android 8 引入了Binder 上下文的概念。每个 Binder 上下文都有自己的设备节点和自己的上下文(服务)管理器。您只能通过设备节点访问上下文管理器,并且当通过特定上下文传递 Binder 节点时,只有来自同一上下文中的另一个进程才能访问它,从而完全隔离网域。如需了解使用详情,请参阅 vndbinder 和 vndservicemanager。
散射-聚集
Common-4.4 及更高版本,包括上游在之前的 Android 版本中,Binder 调用中的每条数据都会被复制三次
- 第一次是在调用进程中将其序列化到
Parcel
中 - 第二次是在内核驱动程序中,将
Parcel
复制到目标进程 - 第三次是在目标进程中反序列化
Parcel
Android 8 使用 散射-聚集优化,将复制次数从 3 次减少到 1 次。数据不再先序列化到 Parcel
中,而是保持其原始结构和内存布局,驱动程序会立即将其复制到目标进程。数据到达目标进程后,其结构和内存布局保持不变,因此无需再次复制即可读取数据。
细粒度锁定
Common-4.4 及更高版本,包括上游在之前的 Android 版本中,Binder 驱动程序使用全局锁来防止并发访问关键数据结构。虽然锁的争用极少,但主要问题在于,如果低优先级线程获得了锁,然后被抢占,则可能会严重延迟需要获得同一锁的高优先级线程。这会导致平台卡顿。
最初尝试解决此问题的方法是在持有全局锁时停用抢占。然而,这与其说是真正的解决方案,不如说是一种权宜之计,最终被上游拒绝并废弃。随后的尝试侧重于使锁定更细粒度,自 2017 年 1 月以来,Pixel 设备上一直运行着一个版本。虽然这些更改中的大部分已公开,但在后续版本中进行了重大改进。
在识别出细粒度锁定实现中的小问题后,我们设计了一种改进的解决方案,采用了不同的锁定架构,并将更改提交到了所有通用内核分支中。我们继续在大量不同的设备上测试此实现;由于我们没有发现任何未解决的问题,因此这是搭载 Android 8 的设备的推荐实现。
实时优先级继承
Common-4.4 和 common-4.9(上游即将推出)Binder 驱动程序一直支持精细的优先级继承。由于 Android 中越来越多进程以实时优先级运行,因此在某些情况下,如果实时线程进行 Binder 调用,则处理该调用的进程中的线程也以实时优先级运行是有意义的。为了支持这些用例,Android 8 现在在 Binder 驱动程序中实现了实时优先级继承。
除了事务级优先级继承之外,节点优先级继承 允许节点(Binder 服务对象)指定调用此节点的最低优先级。以前版本的 Android 已经支持使用 nice 值的节点优先级继承,但 Android 8 添加了对实时调度策略节点继承的支持。
用户空间更改
Android 8 包含了在通用内核中使用当前 Binder 驱动程序所需的所有用户空间更改,但有一个例外:禁用 /dev/binder
的实时优先级继承的原始实现使用了 ioctl。后续开发将优先级继承的控制切换到更精细的方法,该方法是按 Binder 模式(而不是按上下文)进行的。因此,ioctl 不在 Android 通用分支中,而是在 我们的通用内核中提交。
此更改的效果是,默认情况下,每个节点都禁用实时优先级继承。Android 性能团队发现,为 hwbinder
域中的所有节点启用实时优先级继承是有益的。为了达到同样的效果,请在用户空间中 cherry-pick 此更改。
通用内核的 SHA
要获取 Binder 驱动程序的必要更改,请同步到相应的 SHA
- Common-3.18
cc8b90c121de ANDROID: binder: don't check prio permissions on restore. - Common-4.4
76b376eac7a2 ANDROID: binder: don't check prio permissions on restore. - Common-4.9
ecd972d4f9b5 ANDROID: binder: don't check prio permissions on restore.
与 Binder IPC 协同工作
从历史上看,供应商进程一直使用 Binder 进程间通信 (IPC) 进行通信。在 Android 8 中,/dev/binder
设备节点成为框架进程专用的,这意味着供应商进程不再有权访问它。供应商进程可以访问 /dev/hwbinder
,但必须转换其 AIDL 接口以使用 HIDL。对于希望继续在供应商进程之间使用 AIDL 接口的供应商,Android 支持如下所述的 Binder IPC。在 Android 10 中,Stable AIDL 允许所有进程使用 /dev/binder
,同时还解决了 HIDL 和 /dev/hwbinder
解决的稳定性保证问题。有关如何使用 Stable AIDL,请参阅 用于 HAL 的 AIDL。
vndbinder
Android 8 支持一个新的 Binder 域,供供应商服务使用,使用 /dev/vndbinder
而不是 /dev/binder
访问。随着 /dev/vndbinder
的添加,Android 现在有以下三个 IPC 域
IPC 域 | 描述 |
---|---|
/dev/binder |
框架/应用进程之间使用 AIDL 接口的 IPC |
/dev/hwbinder |
框架/供应商进程之间使用 HIDL 接口的 IPC 供应商进程之间使用 HIDL 接口的 IPC |
/dev/vndbinder |
供应商/供应商进程之间使用 AIDL 接口的 IPC |
为了使 /dev/vndbinder
出现,请确保内核配置项 CONFIG_ANDROID_BINDER_DEVICES
设置为 "binder,hwbinder,vndbinder"
(这是 Android 通用内核树中的默认值)。
通常,供应商进程不会直接打开 Binder 驱动程序,而是链接到 libbinder
用户空间库,该库会打开 Binder 驱动程序。为 ::android::ProcessState()
添加一个方法,为 libbinder
选择 Binder 驱动程序。供应商进程应在调用 ProcessState
、IPCThreadState
之前或在进行任何 Binder 调用之前调用此方法。要使用,请在供应商进程(客户端和服务器)的 main()
之后放置以下调用
ProcessState::initWithDriver("/dev/vndbinder");
vndservicemanager
以前,Binder 服务在 servicemanager
中注册,其他进程可以在其中检索它们。在 Android 8 中,servicemanager
现在专供框架和应用进程使用,供应商进程不再可以访问它。
但是,供应商服务现在可以使用 vndservicemanager
,它是 servicemanager
的新实例,它使用 /dev/vndbinder
而不是 /dev/binder
,并且与框架 servicemanager
来自相同的源。供应商进程无需进行更改即可与 vndservicemanager
通信;当供应商进程打开 /dev/vndbinder
时,服务查找会自动转到 vndservicemanager
。
vndservicemanager
二进制文件包含在 Android 的默认设备 makefile 中。
SELinux 策略
希望使用 Binder 功能相互通信的供应商进程需要以下内容
- 访问
/dev/vndbinder
。 - Binder
{transfer, call}
挂钩到vndservicemanager
。 - 对于希望通过供应商 Binder 接口调用供应商域 B 的任何供应商域 A 的
binder_call(A, B)
。 - 在
vndservicemanager
中{add, find}
服务的权限。
要满足要求 1 和 2,请使用 vndbinder_use()
宏
vndbinder_use(some_vendor_process_domain);
要满足要求 3,需要通过 Binder 通信的供应商进程 A 和 B 的 binder_call(A, B)
可以保留在原位,无需重命名。
要满足要求 4,您必须更改服务名称、服务标签和规则的处理方式。
有关 SELinux 的详细信息,请参阅 Android 中的安全增强型 Linux。有关 Android 8.0 中 SELinux 的详细信息,请参阅 Android 8.0 的 SELinux。
服务名称
以前,供应商进程在 service_contexts
文件中注册服务名称,并添加相应的规则来访问该文件。来自 device/google/marlin/sepolicy
的 service_contexts
文件示例
AtCmdFwd u:object_r:atfwd_service:s0 cneservice u:object_r:cne_service:s0 qti.ims.connectionmanagerservice u:object_r:imscm_service:s0 rcs u:object_r:radio_service:s0 uce u:object_r:uce_service:s0 vendor.qcom.PeripheralManager u:object_r:per_mgr_service:s0
在 Android 8 中,vndservicemanager
加载 vndservice_contexts
文件。迁移到 vndservicemanager
的供应商服务(以及已在旧的 service_contexts
文件中的服务)应添加到新的 vndservice_contexts
文件中。
服务标签
以前,服务标签(例如 u:object_r:atfwd_service:s0
)在 service.te
文件中定义。示例
type atfwd_service, service_manager_type;
在 Android 8 中,您必须将类型更改为 vndservice_manager_type
并将规则移动到 vndservice.te
文件。示例
type atfwd_service, vndservice_manager_type;
servicemanager 规则
以前,规则授予域访问权限,以从 servicemanager
添加或查找服务。示例
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;
在 Android 8 中,此类规则可以保留在原位并使用相同的类。示例
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;