在本文中,我将介绍如何为一个新的HIDL服务添加SELinux策略(分享标准和平台方式),以确保它能够在Android系统中正常运行。我将以canbus
服务为例,展示从报错到解决的完整流程。
系列文章:
Android HAL深入探索(2): 传统HAL与文件加解密模拟
Android HAL深入探索(3): HIDL Passthrough模式与串口数据回调模拟
报错分析
历尽千难万苦 好不容易把HAL代码,VNDK 等问题搞定 编译烧录成功! 满心期待的认为服务能够起来,开机之后发现can服务发现没自启动(删除线是错误情况,正确的情况下是有的 所以给你们一个对比:)
rk3568_r:/ # ps -A | grep can
system 279 1 10774936 4732 binder_thread_read 0 S android.hardware.can_bus@1.0-servicerk3568_r:/ # lshal | grep can
FM N android.hardware.can_bus@1.0::ICanBus/default 0/1 279 139
我第一反应就是/system/etc/init/android.hardware.can_bus@1.0-service.rc
没有被启动,所以我尝试 手动执行/system/bin/hw/android.hardware.can_bus@1.0-service
,logcat 报错:
09-09 18:43:59.784 0 0 E init : Control message: Could not ctl.start for 'vendor.hw_canbus' from pid: 6533 (start vendor.hw_canbus): File /system/bin/hw/android.hardware.can_bus@1.0-service(labeled "u:object_r:system_file:s0") has incorrect label or no domain transition from u:r:init:s0 to another SELinux domain defined. Have you configured your service correctly? https://source.android.com/security/selinux/device-policy#label_new_services_and_address_denials 09-09 18:44:00.004 1075 1075 D KeyguardClockSwitch: Updating clock: 下午6:44
结果/system/bin/hw/android.hardware.can_bus@1.0-servicerc
的内容
这个报错告诉我们,init
进程无法启动vendor.hw_canbus
服务,因为它的可执行文件/system/bin/hw/android.hardware.can_bus@1.0-service
没有正确的SELinux标签,或者没有从u:r:init:s0
到另一个SELinux域的转换规则。需要修改SELinux策略,为这个服务定义一个新的域和标签,并且允许它从init
进程启动?我强行关闭SELinux 也无法绕过这个问题,看来是必须解决这个问题,SELinux 至少占了我整个调试过程的一大半时间,我** 你个**。
为了让下次添加顺畅,还有为了大家 能够学习到,我打算总结了个SOP 我理解为最简便的方式。 目标明确 请接着往下看。
方式1:修改Android标准 SELinux策略
学习后了解到 在Android系统中,SELinux策略分为两部分:平台策略和供应商策略。平台策略定义了系统服务和应用程序的规则,供应商策略定义了硬件抽象层(HAL)服务和设备驱动程序的规则。需要修改供应商策略,为canbus
服务添加规则。
Android标准的策略位于system/sepolicy
目录下,其中有以下几个子目录:
在标准Android的SELinux策略结构中,system/sepolicy
目录下的private
、public
和vendor
这三个子目录各自有特定的用途。以下是它们的基本描述和用途:
- public:
这个目录包含了Android平台的公共SELinux策略。这些策略是所有Android设备都必须遵循的。它定义了Android框架和核心系统服务的策略。这些AOSP)的一部分,通常不建议被设备制造商修改。 - private:
这个目录包含了与public
目录中的公共策略配套的私有策略。这些策略通常不应该被外部开发者或设备制造商直接使用或修改。它还包含了一些用于确保策略向后兼容性的文件,如compat
目录下的文件。 - vendor:
这个目录是为设备制造商或SoC供应商提供的,用于定义他们硬件或特定实现的SELinux策略。设备制造商可以在这里添加他们自己的策略,以确保他们的硬件和特定的服务在SELinux强制模式下能够正常工作。这些策略是特定于设备的,可能会因设备而异。
当构建Android系统时,这三个目录中的策略都会被合并,以生成最终的SELinux策略,该策略将在设备上强制执行。
根据实际的需求,需要在这些目录中做以下修改:
vendor目录
- 在
file_contexts
中增加:
/(vendor|system)/bin/hw/android\.hardware\.canbus@1\.0-service u:object_r:hal_canbus_default_exec:s0
- 这一行表示 可执行文件
/vendor/bin/hw/android.hardware.canbus@1.0-service
或者/system/vendor/bin/hw/android.hardware.canbus@1.0-service
(取决于我们把它放在哪里)赋予了一个新的SELinux标签:u:object_r:hal_canbus_default_exec:s0
。这个标签由三部分组成:用户(u),角色(object_r)和类型(hal_canbus_default_exec)。用户和角色通常是固定的,类型是自己定义的。类型的命名规则是:hal_服务名_默认_exec。默认表示这是一个默认的域转换标签,exec表示这是一个可执行文件类型。 - 新建
hal_canbus_default.te
:
type hal_canbus_default, domain; hal_server_domain(hal_canbus_default, hal_canbus) type hal_canbus_default_exec, exec_type, vendor_file_type, file_type; init_daemon_domain(hal_canbus_default)
- 这个文件定义了两个新的类型:
hal_canbus_default
和hal_canbus_default_exec
。前者是服务进程的域类型,后者是服务可执行文件的类型。还使用了一些宏来定义这些类型的属性和规则。这些宏是由供应商策略提供的,可以在system/sepolicy/public/
目录下找到它们的定义。这里使用了以下两个宏:
hal_server_domain(hal_canbus_default, hal_canbus)
: 这个宏定义了一个HAL服务域,它有以下属性:
- 继承了
hal_server
类型的规则,这是一个通用的HAL服务类型,包含了一些基本的权限和约束。 - 允许使用HwBinder进行IPC通信,包括从客户端接收请求,向客户端发送回调,向HwBinder服务管理器注册自己的服务名等。
- 允许访问自己的可执行文件和数据文件。
- 允许访问一些系统属性和日志服务。
- 允许被
init
进程启动和停止。
- 这个宏还需要一个参数,表示这个HAL服务域对应的HAL接口名。在我们的例子中,这个接口名是
hal_canbus
。这个参数会被用来生成一些规则和标签,例如:
type hal_canbus, hal_attribute;
: 这个类型表示一个HAL属性,用来标识这个HAL服务域属于哪个HAL接口。type hal_canbus_hwservice, hwservice_manager_type;
: 这个类型表示一个HwBinder服务类型,用来标识这个HAL服务在HwBinder服务管理器中注册的名字。add_hwservice(hal_canbus_default, hal_canbus_hwservice)
: 这个宏允许这个HAL服务域向HwBinder服务管理器添加自己的服务名。
init_daemon_domain(hal_canbus_default)
: 这个宏定义了一个由init
进程启动的守护进程域,它有以下属性:
- 继承了
init_daemon
类型的规则,这是一个通用的守护进程类型,包含了一些基本的权限和约束。 - 允许被
init
进程创建、启动、停止、重启等。 - 允许读取自己的可执行文件和数据文件。
- 允许访问一些系统属性和日志服务。
public目录
- 在
attributes
中添加:
hal_attribute(canbus);
- 这一行表示,给
canbus
这个HAL接口名赋予了一个新的属性:hal_attribute(canbus)
。这个属性用来标识这个HAL接口属于哪个HAL版本。在Android系统中,有不同版本的HAL接口,例如VINTF和HIDL。不同版本的HAL接口有不同的规则和约束。为了区分它们,需要给每个HAL接口名赋予一个对应的属性。在例子中使用了HIDL作为HAL接口版本,所以给canbus
这个HAL接口名赋予了hal_attribute(canbus)
这个属性。 - 新建
hal_canbus.te
:
# HwBinder IPC from client to server, and callbacks binder_call(hal_canbus_client, hal_canbus_server) binder_call(hal_canbus_server, hal_canbus_client) add_hwservice(hal_canbus_server, hal_canbus_hwservice) allow hal_canbus_client hal_canbus_hwservice:hwservice_manager find;
- 这个文件定义了一些关于HIDL IPC通信的规则。使用了以下几个宏:
binder_call(hal_canbus_client, hal_canbus_server)
: 这个宏允许一个HAL客户端域和一个HAL服务端域之间进行HwBinder IPC通信,包括发送和接收请求和回调。这个宏需要两个参数,分别是客户端域和服务端域的类型。在例子中这两个类型分别是hal_canbus_client
和hal_canbus_server
。这两个类型是由hal_attribute(canbus)
这个属性自动生成的,表示任何使用canbus
这个HAL接口的客户端或服务端域。add_hwservice(hal_canbus_server, hal_canbus_hwservice)
: 这个宏允许一个HAL服务端域向HwBinder服务管理器添加自己的服务名。这个宏需要两个参数,分别是服务端域的类型和服务名的类型。在例子中,这两个类型分别是hal_canbus_server
和hal_canbus_hwservice
。这两个类型是由hal_attribute(canbus)
这个属性自动生成的,表示使用canbus
这个HAL接口的服务端域和服务名。allow hal_canbus_client hal_canbus_hwservice:hwservice_manager find;
: 这个规则允许一个HAL客户端域在HwBinder服务管理器中查找一个HAL服务名。这个规则需要三个参数,分别是客户端域的类型,服务名的类型和HwBinder服务管理器的类。在例子中,这三个参数分别是hal_canbus_client
,hal_canbus_hwservice
和hwservice_manager
。这个规则是为了让客户端能够找到我们的服务,并且建立IPC通信。
private目录
- 在
hwservice_contexts
中添加:
android.hardware.canbus::ICanBus u:object_r:hal_canbus_hwservice:s0
- 这一行表示,给我们的HIDL接口名
android.hardware.canbus::ICanBus
赋予了一个新的SELinux标签:u:object_r:hal_canbus_hwservice:s0
。这个标签由三部分组成:用户(u),角色(object_r)和类型(hal_canbus_hwservice)。用户和角色通常是固定的,类型自己定义的。类型的命名规则是:hal_服务名_hwservice。hwservice表示这是一个HwBinder服务类型。 - 在
private/compat/28.0/28.0.ignore.cil
,private/compat/27.0/27.0.ignore.cil
和private/compat/26.0/26.0.ignore.cil
三个文件中均添加:
hal_canbus_hwservice
- 这一行表示,我们将我们的HwBinder服务类型
hal_canbus_hwservice
添加到了兼容性忽略列表中。这是为了避免在不同API级别之间进行SELinux策略检查时出现错误。因为我们的服务是一个新的HIDL接口,所以它不需要在旧的API级别中存在。
同步到prebuilts
最后一步我们需要将在public
和private
目录中的修改同步到prebuilts/api/30.0/public
和prebuilts/api/30.0/private
目录下。这是为了确保平台策略能够引用我们定义的公共类型和规则,并且能够通过SELinux策略检查。
可以使用以下命令进行sync:
cp vendor/sepolicy/public/attributes prebuilts/api/30.0/public/attributes cp vendor/sepolicy/public/hal_canbus.te prebuilts/api/30.0/public/hal_canbus.te cp vendor/sepolicy/private/hwservice_contexts prebuilts/api/30.0/private/hwservice_contexts cp vendor/sepolicy/private/compat/26.0/26.0.ignore.cil prebuilts/api/30.0/private/compat/26.0/26.0.ignore.cil cp vendor/sepolicy/private/compat/27.0/27.0.ignore.cil prebuilts/api/30.0/private/compat/27.0/27.0.ignore.cil cp vendor/sepolicy/private/compat/28.0/28.0.ignore.cil prebuilts/api/30.0/private/compat/28.0/28.0.ignore.cil
modified: system/sepolicy/prebuilts/api/30.0/private/compat/26.0/26.0.ignore.cil modified: system/sepolicy/prebuilts/api/30.0/private/compat/27.0/27.0.ignore.cil modified: system/sepolicy/prebuilts/api/30.0/private/compat/28.0/28.0.ignore.cil modified: system/sepolicy/prebuilts/api/30.0/private/compat/29.0/29.0.ignore.cil modified: system/sepolicy/prebuilts/api/30.0/private/hwservice_contexts modified: system/sepolicy/prebuilts/api/30.0/public/attributes modified: system/sepolicy/prebuilts/api/30.0/public/domain.te modified: system/sepolicy/private/compat/26.0/26.0.ignore.cil modified: system/sepolicy/private/compat/27.0/27.0.ignore.cil modified: system/sepolicy/private/compat/28.0/28.0.ignore.cil modified: system/sepolicy/private/compat/29.0/29.0.ignore.cil modified: system/sepolicy/private/hwservice_contexts modified: system/sepolicy/public/attributes modified: system/sepolicy/public/domain.te modified: system/sepolicy/vendor/file_contexts modified: system/sepolicy/public/hal_canbus.te modified: system/sepolicy/vendor/hal_canbus_default.te
方式2:修改平台为新的HAL服务添加SELinux策略
1. 定义新的SELinux类型和权限:
- 创建一个新的SELinux策略文件
device/rockchip/common/sepolicy/vendor/hal_can_bus_default_vndk.te
。 - 在此文件中,定义了新的SELinux类型
hal_can_bus_default
和hal_can_bus_default_exec
。 - 为
hal_can_bus_default
定义了与硬件抽象层(HAL)相关的权限。 - 允许
hal_can_bus_default
访问serial_device
。
type hal_can_bus_default, domain; hal_server_domain(hal_can_bus_default, hal_can_bus) type hal_can_bus_default_exec, exec_type, vendor_file_type, file_type; init_daemon_domain(hal_can_bus_default) allow hal_can_bus_default serial_device:chr_file rw_file_perms;
2. 更新hwservice_contexts:
- 修改
device/rockchip/common/sepolicy/vendor/hwservice_contexts
文件。 - 为新的HAL服务
android.hardware.can_bus::ICanBus
添加了一个SELinux上下文。
+android.hardware.can_bus::ICanBus u:object_r:hal_can_bus_hwservice:s0
3. 更新文件上下文:
- 修改
device/rockchip/common/sepolicy/vendor/file_contexts
文件。 - 为新的HAL服务可执行文件
android.hardware.can_bus@1.0-service
定义了一个SELinux上下文。
+/(vendor|system/vendor)/bin/hw/android\.hardware\.can_bus@1\.0-service u:object_r:hal_can_bus_default_exec:s0
4. 修改HAL的构建配置:
- 修改
hardware/interfaces/can_bus/1.0/service/Android.bp
文件。 - 将HAL服务的安装位置从
system
分区更改为vendor
分区,这是通过添加vendor: true
来实现的。
cc_binary { name: "android.hardware.can_bus@1.0-service", relative_install_path: "hw", init_rc: ["android.hardware.can_bus@1.0-service.rc"], srcs: [ "CanBusService.cpp", ], shared_libs: [ "liblog", "libutils", "libhidlbase", "libhwbinder", "android.hardware.can_bus@1.0", ], + vendor: true, }
5. 更新服务的init.rc文件:
- 修改
android.hardware.can_bus@1.0-service.rc
文件。 - 将服务的路径更改为
/vendor/bin/hw/android.hardware.can_bus@1.0-service
,以确保它在vendor
分区中正确启动。
+service vendor.hw_canbus /vendor/bin/hw/android.hardware.can_bus@1.0-service
这些步骤确保了新的HAL服务正确地在vendor
分区中部署,并具有适当的SELinux上下文和权限。这是为特定设备或硬件平台定制Android SELinux策略的典型方法。
方式3: 修改平台为新的HAL服务添加SELinux策略(项目版)
参考方式2的基础 , 我们应该在 device/rockchip/rk356x/sepolicy/vendor/hwservice_contexts 文件中添加相应的内容,而不是在 common 目录下。这样做是为了避免与其他设备或平台的政策文件发生冲突,并且可以更容易地识别和维护自己的政策文件。(假设rk356x 就是project名字)
1. 定义新的SELinux类型和权限:
- 在
device/rockchip/rk356x/sepolicy/vendor
目录下,创建一个新的SELinux策略文件hal_can_bus_default_vndk.te
。
2. 更新hwservice_contexts:
- 在
device/rockchip/rk356x/sepolicy/vendor
目录下,修改或创建hwservice_contexts
文件。
3. 更新文件上下文:
- 在
device/rockchip/rk356x/sepolicy/vendor
目录下,修改或创建file_contexts
文件。
4. 修改BoardConfig.mk:
- 在
BoardConfig.mk
中,添加以下变量来指定SELinux策略文件的位置和内容。
BOARD_SEPOLICY_VENDOR_DIRS += \ <root>/device/rockchip/rk356x/sepolicy/vendor BOARD_SEPOLICY_VENDOR_UNION += \ file_contexts \ hal_can_bus_default_vndk.te \ hwservice_contexts
也可以参考device/rockchip$ vi common/BoardConfig.mk +252
在自己的项目里添加sepolicy。
了解,我将为您提供更详细的步骤,并为每个代码内容加上中文注释。
方式4: 为新的Smart Home HAL服务添加SELinux策略(项目版)
参考方式3的基础,我们在 hardware/interfaces/smart_home/sepolicy
中添加相应的内容,而不是在 common 和 system目录下。这样做是为了避免与其他设备或平台的政策文件发生冲突,并且可以更容易地识别和维护自己项目的政策文件。(
1. 定义新的SELinux类型和权限:
- 在
hardware/interfaces/smart_home/sepolicy
目录下,创建一个新的SELinux策略文件smart_home.te
。
# 定义smart_home的域 type smart_home, domain; # 定义smart_home的执行类型和文件类型 type smart_home_exec, exec_type, vendor_file_type, file_type; # 允许smart_home使用hwbinder hwbinder_use(smart_home); # 初始化smart_home的守护进程域 init_daemon_domain(smart_home) # 添加硬件服务 add_hwservice(smart_home, smart_home_hwservice) # 允许smart_home读取hwservicemanager的属性文件 allow smart_home hwservicemanager_prop:file {read open getattr map}; # 允许smart_home读取系统文件目录 allow smart_home system_file:dir {read open getattr search};
2. 更新hwservice_contexts:
- 在
hardware/interfaces/smart_home/sepolicy
目录下,修改或创建hwservice_contexts
文件。
# 定义smart_home的硬件服务上下文 android.hardware.smart_home::Ismart_home u:object_r:smart_home_hwservice:s0
3. 更新文件上下文:
- 在
hardware/interfaces/smart_home/sepolicy
目录下,修改或创建file_contexts
文件。
# 定义smart_home的二进制文件上下文 /(vendor|system/vendor)/bin/hw/android\.hardware\.smart_home@1\.0-service u:object_r:smart_home_exec:s0 # 定义smart_home的库文件上下文 /(vendor|system/vendor)/lib(64)?/hw/android\.hardware\.smart_home@1\.0-impl\.so u:object_r:same_process_hal_file:s0
4. 修改customize.mk:
- 在
vendor/customize/customize.mk
中,添加以下变量来指定SELinux策略文件的位置和内容:
# 指定SELinux策略文件的目录 BOARD_SEPOLICY_DIRS += \ hardware/interfaces/smart_home/sepolicy
Android的SELinux策略结构的区别
在Android的SELinux策略结构中,system/sepolicy
和device/<manufacturer>/<device>/sepolicy
都是用于定义和定制SELinux策略的位置。但它们之间有一些关键的区别和用途:
- system/sepolicy:
这是AOSP提供的默认SELinux策略的位置。它定义了Android系统的基本安全策略。通常不建议直接修改这些文件,因为它们是AOSP的一部分,为了避免耦合,调试困难建议修改平台的. - device///sepolicy:
这是为特定设备或硬件制造商定制SELinux策略的位置。通常是为了支持特定设备的硬件或功能。在编译时会与system/sepolicy
中的策略合并,以形成完整的SELinux策略。
合并过程:
在Android的构建过程中,system/sepolicy
和device/<manufacturer>/<device>/sepolicy
中的策略文件会被合并在一起。这是通过Android的build构建系统自动完成的。如果device/<manufacturer>/<device>/sepolicy
中的策略与system/sepolicy
中的策略冲突,构建过程会失败。
为什么在device下也可以添加成功?:
当在device/<manufacturer>/<device>/sepolicy
中添加策略时,实际上是在为特定设备添加或覆盖默认策略。我查阅资料和实践 发现是可以的,实际上这是推荐的方法来为特定设备定制SELinux策略,而不是直接修改system/sepolicy
。
system/sepolicy
提供了Android系统的默认SELinux策略,而device/<manufacturer>/<device>/sepolicy
允许硬件制造商和设备维护者为特定设备定制这些策略。在构建过程中,这两个位置的策略会被合并在一起。
Android系统和供应商分区的区别及其重要性
/system
vs /vendor
的区别
/system
:这个分区包含了Android操作系统的主要部分,包括系统应用、框架、库和其他核心代码。为了安全性和稳定性,这个分区在正常操作中是只读的。/vendor
:这个分区包含了设备制造商的硬件相关代码和二进制文件。这使得制造商可以独立于主Android操作系统更新他们的硬件支持代码,从而加速设备的更新速度。
如果不明确设置vendor: true
,构建系统会将把模块放在/system
分区。
/system
vs /vendor
的访问隔离
在Android 8.0及更高版本中,为了支持Project Treble,Android的目录结构和访问控制经历了重大变化。将硬件相关的代码和服务放在/vendor
中意味着设备制造商可以更新硬件支持代码,而不需要等待整个Android OS的更新。
/vendor
分区被设计为与/system
分区隔离。这意味着/vendor
中的代码不应该依赖于/system
中的库,反之亦然。
# 这个错误是我手动将system移动到vendor 直接移动是不行的,要改Android.bp + vendor: true, vendor/bin/hw/android.hardware.can_bus@1.0-service CANNOT LINK EXECUTABLE "vendor/bin/hw/android.hardware.can_bus@1.0-service": library "libhwbinder.so" not found: needed by main executable
如果遇到的错误是因为/vendor
中的可执行文件试图链接到一个在/system
中的库,这是不允许的。为了解决这个问题,需要确保所有/vendor
中的可执行文件和服务所需的库也都位于/vendor
中。
从SELinux策略的角度看,/system
和/vendor
也是隔离的。这意味着/system
中的进程默认情况下不能访问/vendor
中的资源,反之亦然。
测试和验证
经过以上的修改,就完成了为canbus
服务添加SELinux策略的过程。为了测试和验证修改是否正确,可以重新构建的系统,并且在设备上运行以下命令:
ls -Z /vendor/bin/hw/android.hardware.can_bus@1.0-service
: 这个命令可以查看我们的服务可执行文件的SELinux标签是u:object_r:hal_can_bus_default_exec:s0
。
rk3568_r:/vendor # ls -Z /vendor/bin/hw/android.hardware.can_bus@1.0-service u:object_r:hal_can_bus_default_exec:s0 /vendor/bin/hw/android.hardware.can_bus@1.0-service
ps -AT | grep can_bus
: 这个命令可以查看服务进程的SELinux标签,应该是u:r:hal_can_bus_default:s0
。
rk3568_r:/vendor # ps -AZ | grep can_bus u:r:hal_can_bus_default:s0 system 279 1 10774936 4732 binder_thread_read 0 S android.hardware.can_bus@1.0-service
dmesg | grep avc
: 这个命令可以查看是否有任何SELinux拒绝日志,应该没有与服务相关的拒绝。lshal debug -E android.hardware.canbus@1.0::ICanBus/default
: 这个命令可以查看服务的详细信息,包括它的HwBinder句柄,引用计数,调试信息等。
rk3568_r:/vendor # lshal debug -E android.hardware.canbus@1.0::ICanBus/default android.hardware.canbus@1.0::ICanBus/default does not exist, or no permission to connect. 128|rk3568_r:/vendor # lshal | grep can_bus FM N android.hardware.can_bus@1.0::ICanBus/default 0/1 279 139
如果以上的命令都能正常运行,并且没有出现任何错误或异常,那么就可以认为成功地为canbus
服务添加了SELinux策略。
总结
前前后后 学习这个权限和验证花了很多时间,期间在方式1耗时最多 ,为了写笔记 尝试了好几次搞完就checkout了 , 方式1 我现在还有一点迷迷糊糊的 是验证OK了,但还是被我checkout 最终用的方式3。
在本文中,我介绍了如何为一个新的HIDL服务添加SELinux策略(分享标准和平台方式),以确保它能够在Android系统中正常运行。我以canbus
服务为例,展示了从报错到解决的完整流程。我希望这篇文章能够对你有所帮助,如果你有任何问题或建议,请在评论区留言。谢谢!