Android Binder【篇六:AIDL HAL避坑与SELinux配置】
前文已经介绍了硬件抽象层中的HIDL接口,然而在Android 14之后,Google计划废弃HIDL接口,转而使用AIDL进行替换。在Android 16之后,Google已经作为强制策略,并使用XTS进行拦截,如下vts_treble_no_hidl失败项日志:

因此基于上一篇介绍的自定义HIDL HAL进程的基础上,本篇介绍一下如何从零开始新增一个AIDL HAL进程,或者如何把之前HIDL HAL转换为一个AIDL HAL进程。
本文将重点解决以下三大核心难题:
- 编译构建: 如何处理
aidl_interface的unstable与stability配置,解决 API dump 不存在的编译报错。 - 运行时通信: 如何配置 VINTF 兼容性矩阵,解决
ServiceManager.waitForDeclaredService返回 NULL 的问题。 - SELinux 权限: 如何编写
.te策略文件,解决avc: denied { add/find }的权限拒绝崩溃。
在开始之前,先送上两份避坑指南,建议先收藏再看:
-
AIDL HAL 开发常见报错速查表
| 报错现象 (Logcat) | 根本原因 | 解决方案关键词 |
|---|---|---|
API dump for current version... does not exist |
接口未冻结/未声明不稳定 | 添加 unstable: true 或执行 m xxx-update-api |
Cannot do a user transaction... vendor stability |
接口稳定性不匹配 | 添加 stability: "vintf" 并冻结 API |
ServiceManager.waitForDeclaredService is NULL |
VINTF 兼容性检查失败 | 修改 framework_compatibility_matrix.xml |
avc: denied { add } (服务端) |
SELinux 策略拒绝注册 | 定义 hal_server_domain 和 hal_attribute_service |
avc: denied { find } (客户端) |
SELinux 策略拒绝查找 | 在客户端域添加 hal_client_domain |
-
Selinux核心配置对照表
| 配置文件 | 关键配置项 | 作用说明 |
|---|---|---|
file_contexts |
...:hal_oemsarsensor_aidl_exec |
给可执行文件打上标签,允许 init 域转换 |
xxx.te |
hal_attribute(oemsarsensor) |
定义 HAL 的属性簇 (client/server) |
xxx.te |
hal_server_domain(...) |
赋予服务端进程 Binder 通信和注册能力 |
service_contexts |
...:hal_oemsarsensor_service |
定义服务名在 ServiceManager 中的客体类别 |
一、IThermal AIDL
在介绍如何自定义AIDL HAL之前,我们先来看看AOSP原生是如何实现一个AIDL的,它与存在已久的HIDL HAL之间又是如何共存的,那么本章还是拿之前的IThermal作为一学习案例。
1、IThermal AIDL服务端进程

同样先看看IThermal HAL的整体架构,如上截图,aidl和hidl的接口都放到一起的,因此他们是可以共存的。因此我在移植aidl的时候,也是在被移植的模块的HAL目录下去创建一个aidl目录。
pengcheng.ding@xxx:~/works/ascom-rkp/os/la.um/hardware/interfaces/thermal$ ls
1.0 1.1 2.0 aidl OWNERS utils
pengcheng.ding@xxx:~/works/ascom-rkp/os/la.um/hardware/interfaces/thermal$ tree
.
├── 1.0
├── 1.1
├── 2.0
├── aidl
│ ├── aidl_api
│ │ └── android.hardware.thermal
│ │ ├── 1
│ │ │ └── android
│ │ │ └── hardware
│ │ │ └── thermal
│ │ │ ├── CoolingDevice.aidl
│ │ │ ├── CoolingType.aidl
│ │ │ ├── IThermal.aidl
│ │ │ ├── IThermalChangedCallback.aidl
│ │ │ ├── Temperature.aidl
│ │ │ ├── TemperatureThreshold.aidl
│ │ │ ├── TemperatureType.aidl
│ │ │ └── ThrottlingSeverity.aidl
│ │ ├── 2
│ │ │ └── android
│ │ │ └── hardware
│ │ │ └── thermal
│ │ │ ├── CoolingDevice.aidl
│ │ │ ├── CoolingType.aidl
│ │ │ ├── ICoolingDeviceChangedCallback.aidl
│ │ │ ├── IThermal.aidl
│ │ │ ├── IThermalChangedCallback.aidl
│ │ │ ├── Temperature.aidl
│ │ │ ├── TemperatureThreshold.aidl
│ │ │ ├── TemperatureType.aidl
│ │ │ └── ThrottlingSeverity.aidl
│ │ ├── 3
│ │ │ └── android
│ │ │ └── hardware
│ │ │ └── thermal
│ │ │ ├── CoolingDevice.aidl
│ │ │ ├── CoolingType.aidl
│ │ │ ├── ICoolingDeviceChangedCallback.aidl
│ │ │ ├── IThermal.aidl
│ │ │ ├── IThermalChangedCallback.aidl
│ │ │ ├── Temperature.aidl
│ │ │ ├── TemperatureThreshold.aidl
│ │ │ ├── TemperatureType.aidl
│ │ │ └── ThrottlingSeverity.aidl
│ │ └── current
│ │ └── android
│ │ └── hardware
│ │ └── thermal
│ │ ├── CoolingDevice.aidl
│ │ ├── CoolingType.aidl
│ │ ├── ICoolingDeviceChangedCallback.aidl
│ │ ├── IThermal.aidl
│ │ ├── IThermalChangedCallback.aidl
│ │ ├── Temperature.aidl
│ │ ├── TemperatureThreshold.aidl
│ │ ├── TemperatureType.aidl
│ │ └── ThrottlingSeverity.aidl
│ ├── android
│ │ └── hardware
│ │ └── thermal
│ │ ├── CoolingDevice.aidl
│ │ ├── CoolingType.aidl
│ │ ├── ICoolingDeviceChangedCallback.aidl
│ │ ├── IThermal.aidl
│ │ ├── IThermalChangedCallback.aidl
│ │ ├── Temperature.aidl
│ │ ├── TemperatureThreshold.aidl
│ │ ├── TemperatureType.aidl
│ │ └── ThrottlingSeverity.aidl
│ ├── Android.bp
│ ├── default
│ │ ├── Android.bp
│ │ ├── apex_file_contexts
│ │ ├── apex_manifest.json
│ │ ├── main.cpp
│ │ ├── Thermal.cpp
│ │ ├── thermal-example.rc
│ │ ├── thermal-example.xml
│ │ └── Thermal.h
│ └── vts
│ ├── Android.bp
│ └── VtsHalThermalTargetTest.cpp
├── OWNERS
└── utils
1)aidl_interface接口定义
在aidl主目录下首先需要做的事情就是Android.bp来定义声明AIDL接口,google官方网站已经提供了一个案例:
参考https://source.android.com/docs/core/architecture/aidl/stable-aidl?hl=zh-cn,google还提供了一个示例代码:
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions_with_info: [
{
version: "1",
imports: ["other-aidl-V1"],
},
{
version: "2",
imports: ["other-aidl-V3"],
}
],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
rust: {
enabled: true,
},
},
}
name:AIDL 接口模块的名称,能唯一标识 AIDL 接口。srcs:组成接口的 AIDL 源文件的列表。软件包com.acme中定义的 AIDL 类型Foo的路径应为<base_path>/com/acme/Foo.aidl,其中<base_path>可以是与Android.bp所在目录相关的任何目录。在以上示例中,<base_path>为srcs/aidl。local_include_dir:软件包名称以此路径开头。此路径与上述<base_path>相对应。imports:此接口使用的aidl_interface模块的列表。如果您的其中一个 AIDL 接口使用另一个aidl_interface中的接口或 Parcelable,请在此处输入其名称。这可以是名称本身(用于指最新版本),也可以是带有版本后缀的名称(例如-V1,用于指特定版本)。从 Android 12 开始已支持指定版本versions:冻结在api_dir下的接口的先前版本。从 Android 11 开始,versions冻结在aidl_api/name下。如果没有已冻结的接口版本,就不应指定此属性,且不会进行兼容性检查。对于 Android 13 及更高版本,此字段已替换为versions_with_info。versions_with_info:元组列表,每个元组包含一个冻结版本的名称和一个列表,其中包含此版本的 aidl_interface 导入的其他 aidl_interface 模块的版本导入。AIDL 接口 IFACE 版本 V 的定义位于aidl_api/IFACE/V。此字段是在 Android 13 中引入的,不支持直接在Android.bp中修改。通过调用*-update-api或*-freeze-api可添加或更新此字段。此外,当用户调用*-update-api或*-freeze-api时,versions字段会自动迁移到versions_with_info。stability:可选标志,用于承诺此接口的稳定性。仅支持"vintf"。如果未设置stability,除非指定unstable,否则构建系统会检查接口是否向后兼容。如果未设置此属性,则意味着接口在此编译环境(所有系统元素,例如system.img和相关分区中的元素,或所有供应商元素,例如vendor.img和相关分区中的元素)下具有稳定性。如果将stability设为"vintf",这表示做出了稳定性承诺:只要有代码使用此接口,此接口就必须保持稳定。gen_trace:可选标志,用于开启或关闭跟踪功能。从 Android 14 开始,cpp和java后端的默认值为true。host_supported:可选标志,设置为true时,可将生成的库提供给主机环境。unstable:可选标志,用于标记此接口不需要稳定。如果将此标志设置为true,构建系统既不会为接口创建 API 转储,也不需要更新接口。frozen:可选标志,设置为true表示接口自上一个接口版本以来没有任何更改。这样可以启用更多构建时检查。设置为false表示接口处于开发阶段且包含新更改,因此运行foo-freeze-api将会生成新版本并自动将值更改为true。在 Android 14 中引入。backend.<type>.enabled:这些标志用于切换后端的启用状态,AIDL 编译器会为这些后端生成代码。支持四个后端:Java、C++、NDK 和 Rust。Java、C++ 和 NDK 后端默认处于启用状态。如果这三个后端中有任何一个不是必需的,则需要明确将其停用。Rust 默认处于停用状态,直到 Android 15。backend.<type>.apex_available:生成的桩库支持的 APEX 名称的列表。backend.[cpp|java].gen_log:可选标志,用于控制是否生成其他代码以收集有关事务的信息。backend.[cpp|java].vndk.enabled:可选标记,用于将此接口变为 VNDK 的一部分。默认值为false。backend.[cpp|ndk].additional_shared_libraries:在 Android 14 中引入,此标志用于向原生库添加依赖项。此标志对ndk_header和cpp_header非常有用。backend.java.sdk_version:可选标志,用于指定构建 Java 桩库所基于的 SDK 版本。默认设置为"system_current"。如果backend.java.platform_apis为true,就不应设置此标志。backend.java.platform_apis:可选标志,当生成的库需要针对平台 API 而非 SDK 进行构建时,应将此标志设置为true。
最后我们来check一下IThermal AIDL接口的定义:

2)AIDL文件编写
aidl_interface指定了接口模块的定义和编译,因此我们下一步就是针对srcs参数指定的XXX.aidl文件进行编写,先看看IThermal模块的实现:


接下来在看看aidl_api目录下的接口是什么呢?



aidl_api目录下的内容其实是在配置在冻结AIDL接口的时候自动生成,详细可以参考后文。
最后针对AIDL文件的语法, 详细的可以参考google官方文档:
https://source.android.com/docs/core/architecture/aidl/aidl-language?hl=zh-cn
3)AIDL服务端进程定义
在前面两节中,我们看到了AIDL接口的定义和AIDL文件的编写,那么接下来需要了解的就是AIDL服务端进程是如何实现的?同HIDL的服务端进程类似,通常做法创建default目录,在此目录下实现服务端native进程:


如上Android.bp里面有几个关键的参数配置:
- relative_install_path:设置为hw表示编译之后的可执行bin文件安装在hw目录
- vendor:设置为true表示编译之后的输出路径在vendor分区
- init_rc:指定hw native进程rc启动脚本,上文通过prebuilt_etc实现
- vintf_fragments:指定设备清单的xml路径,上文通过prebuilt_etc实现
- static_libs:引入aidl的ndk接口,即aidl_interface中配置了backend ndk,因此AIDL接口会被编译成"aidl包名-V版本号-ndk",只有引入这个静态库,在native服务进程才能使用这个AIDL接口。值得注意的是,在冻结AIDL之前和冻结之后,这个so库是不一样的。
- shared_libs:引入libbinder_ndk动态库,这也是必须的,原因同上一样。
4)aidl ndk库为什么必须要引入?
前面介绍了static_libs的配置,必须要引入AIDL文件编译出来的ndk动态库,因为在服务端进程最终的代码实现如下:


5)AIDL服务端进程主函数
最后回到IThermal AIDL服务端进程主函数代码看看,和HIDL服务端主进程类似,创建binder线程池,实例化接口对象,注册为系统级服务,差别就是服务注册使用的函数不一样:

服务端进程的启动同HIDL基本一致:

2、IThermal AIDL客户端调用
本节在继续看看客户端是怎么对AIDL HAL服务进程进行通信的呢?同样拿IThermal的案例,我们再次回到ThermalManagerService.java相关代码进行定位。
1)aidl java库引入
ThermalManagerService属于framework/base里面的代码,同之前讲解的HIDL接口一样,客户端调用首先需要做的就是引入java库,我们来看看android.hardware.thermal的java库是如何引入的:

thermal的这个案例是在framework-non-updatable-sources中集成的android.hardware.thermal-V3-java-source,和android.hardware.thermal-V3-java的区别如下:
| 名称 | 大致含义 |
|---|---|
| …-V3-java | 编译后的 Java 库(正常依赖用) |
| …-V3-java-source | 生成/打包的 Java 源码(特殊构建或工具链用) |
2)aidl接口调用
那么ThermalManagerService作为客户端,是如何通过binder的方式,与服务端AIDL HAL进程进行通信的?
//os/la.qssi16/frameworks/base/services/core/java/com/android/server/power/ThermalManagerService.java
import android.hardware.thermal.IThermal;
import android.hardware.thermal.IThermalChangedCallback;
public class ThermalManagerService extends SystemService {
@VisibleForTesting
static class ThermalHalAidlWrapper extends ThermalHalWrapper implements IBinder.DeathRecipient {
//AIDL接口,用来与AIDL HAL服务端进行通信 Proxy object for the Thermal HAL AIDL service.
@GuardedBy("mHalLock")
private IThermal mInstance = null;
private IThermal getHalInstance() {
synchronized (mHalLock) {
return mInstance;
}
}
//AIDL回调接口,用来接收来自AIDL HAL服务端的调用 Callback for Thermal HAL AIDL.
private final IThermalChangedCallback mThermalCallbackAidl =
new IThermalChangedCallback.Stub() {
@Override
public void notifyThrottling(android.hardware.thermal.Temperature temperature) { }
@Override
public void notifyThresholdChanged(TemperatureThreshold threshold) { }
@Override
public int getInterfaceVersion() throws RemoteException { return this.VERSION; }
@Override
public String getInterfaceHash() throws RemoteException { return this.HASH; }
};
ThermalHalAidlWrapper(WrapperThermalChangedCallback callback) {
mCallback = callback;
}
//通过AIDL接口与AIDL HAL服务端进程进行通信
protected float forecastSkinTemperature(int forecastSeconds) {
//拿到AIDL HAL服务端进程的接口
final IThermal instance = getHalInstance();
if (instance == null) return Float.NaN;
try {
//调用AIDL HAL服务端forecastSkinTemperature接口
//其实就是通过binder方式像服务端进程发送命令
return instance.forecastSkinTemperature(forecastSeconds);
} catch (RemoteException e) {
//捕获RemoteException异常表示与服务端断开连接,因此这里做了重连机制
synchronized (mHalLock) { connectToHalIfNeededLocked(instance); }
}
return Float.NaN;
}
//连接AIDL HAL服务端进程
protected boolean connectToHal() {
synchronized (mHalLock) {
return connectToHalIfNeededLocked(mInstance);
}
}
@GuardedBy("mHalLock")
protected boolean connectToHalIfNeededLocked(IThermal instance) {
if (instance != mInstance) return true;
//核心代码:获取AIDL HAL服务接口
IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
IThermal.DESCRIPTOR + "/default"));
//核心代码:转换AIDL HAL服务接口
initProxyAndRegisterCallbackLocked(binder);
return mInstance != null;
}
@VisibleForTesting
void initProxyAndRegisterCallback(IBinder binder) {
synchronized (mHalLock) { initProxyAndRegisterCallbackLocked(binder); }
}
//注册AIDL HAL回调接口并进行死亡监听处理
@GuardedBy("mHalLock")
protected void initProxyAndRegisterCallbackLocked(IBinder binder) {
if (binder != null) {
//转换得到AIDL HAL服务对象
mInstance = IThermal.Stub.asInterface(binder);
try {
//对binder对象进行死亡监听注册,第一个参数是IBinder.DeathRecipient接口
binder.linkToDeath(this, 0);
//如果对端死亡或者连接断开,就会回调binderDied方法
//因此常规做法就是在binderDied中调用connectToHal进行重连操作
} catch (RemoteException e) {
connectToHal();
}
if (mInstance != null) {
//获取AIDL接口版本号
try {
Slog.i(TAG, "Thermal HAL AIDL service connected with version " + mInstance.getInterfaceVersion());
} catch (RemoteException e) { connectToHal(); return; }
//注册AIDL接口回调函数
try {
mInstance.registerThermalChangedCallback(mThermalCallbackAidl);
} catch (IllegalArgumentException | IllegalStateException e) {
Slog.e(TAG, "Couldn't registerThermalChangedCallback due to invalid status", e);
} catch (RemoteException e) {
Slog.e(TAG, "Unable to connect IThermal AIDL instance", e);
connectToHal();
}
}
}
}
@Override
public synchronized void binderDied() {
Slog.w(TAG, "Thermal AIDL HAL died, reconnecting...");
connectToHal();
}
}
}
如上代码完美的演示了如下几个流程:
- AIDL客户端进程如何去获取AIDL HAL服务端进程的句柄,通过这个句柄单向调用服务端
- AIDL客户端进程如何通过这个句柄去注册AIDL回调接口首先单向接收服务端的数据
- AIDL死亡监听,连接断开自动重连保持binder通信的稳定性
其实和之前讲到的HIDL基本一致,比较大的区别就是接口的获取方式发生了变化:
binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
IThermal.DESCRIPTOR + "/default"));
mInstance = IThermal.Stub.asInterface(binder);
| API | 含义(通俗) |
|---|---|
| waitForDeclaredService(name) | 等服务注册好再拿 Binder,名字应对应 已声明 的服务;适合解决「服务还没起来」的问题。 |
| allowBlocking(binder) | 允许当前线程对该 Binder 做阻塞式同步调用;解决「拿到的 IBinder 默认不允许阻塞」的策略问题。 |
二、AIDL HAL避坑指南
第一章介绍了AOSP一个比较经典的案例,那么本章开始就模仿这套机制来自定义一套新的AIDL HAl。本篇以之前自定义的oemsarsensor的HIDL为案例进行说明,oemsarsensor的目录结构如下:
pengcheng.ding@xxx110:~/works/x81x/os/la.um/vendor/tinno$ ls
hardware product
pengcheng.ding@xxx110:~/works/x81x/os/la.um/vendor/tinno$ tree
.
├── hardware
│ └── interfaces
│ ├── Android.bp
│ ├── current.txt
│ ├── oemsarsensor
│ │ ├── 1.0
│ │ │ ├── Android.bp
│ │ │ ├── default
│ │ │ │ ├── Android.bp
│ │ │ │ ├── OemSarSensor.cpp
│ │ │ │ ├── OemSarSensor.h
│ │ │ │ ├── service.cpp
│ │ │ │ ├── vendor.tinno.hardware.oemsarsensor@1.0-service.rc
│ │ │ │ └── vendor.tinno.hardware.oemsarsensor@1.0-service.xml
│ │ │ ├── IOemSarSensor.hal
│ │ │ └── types.hal
│ │ └── Android.bp
1、AIDL的接口定义
同IThermal的案例一样,我在oemsarsensor目录下创建aidl目录,并在aidl目录创建Android.bp并开始写我的aidl_interface配置:
// AIDL interface for vendor.tinno.hardware.oemsarsensor (converted from HIDL)
aidl_interface {
name: "vendor.tinno.hardware.oemsarsensor",
visibility: ["//visibility:public"],
srcs: [
"vendor/tinno/hardware/oemsarsensor/Status.aidl",
"vendor/tinno/hardware/oemsarsensor/IOemSarSensor.aidl",
],
vendor_available: true,
backend: {
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
java: {
enabled: true,
},
},
}
PS:注意我这里其实并没有去配置stability frozen versions_with_info unstable属性
1)创建aidl文件
在aidl_interface里面已经配置了两个接口,其路径vendor/tinno/hardware/oemsarsensor,因此在aidl根目录创建这样的路径,最后创建两个aidl文件:
// 创建IOemSarSensor.aidl文件
// Tinno add pengcheng.ding: AIDL conversion from HIDL IOemSarSensor
package vendor.tinno.hardware.oemsarsensor;
import vendor.tinno.hardware.oemsarsensor.Status;
/** OemSarSensor HAL interface (AIDL). */
interface IOemSarSensor {
/** Set SAR sensor mode. */
Status setSarSensorMode(int mode);
/** Set vibrator enable */
Status setVibraterEnable(int mode);
/** Set charge limit */
Status setChargeLimit(int limit);
/** Set charge no limit */
Status setChargeNoLimit(int limit);
}
// 创建Status.aidl文件
package vendor.tinno.hardware.oemsarsensor;
/** Status enum matching HIDL vendor.tinno.hardware.oemsarsensor@1.0::Status */
enum Status {
SUCCESS,
FAILED,
UNKNOWN
}
PS:注意这里的接口包名要和aidl_interface配置的一样;这里缺少@VintfStability注解
2)hidl2aidl工具转换
如果你不想手动编辑,你可以使用AI Cursor帮你自动生成,除此之外,你还可以使用AOSP提供的默认工具hidl2aidl进行转换,大致步骤如下:
- 步骤一:编译hidl2aidl工具
source build/envsetup.sh
lunch xxx
m hidl2aidl
- 步骤二:HIDL转换AIDL
hidl2aidl --->在aosp根目录执行
-o vendor/tinno/hardware/interfaces/googlekey/aidl --->被转换的aidl目录路径
-r vendor.tinno.hardware:vendor/tinno/hardware/interfaces --->指定接口包名和路径
vendor.tinno.hardware.googlekey@1.0 --->指定hidl的包名和版本号
---->最后在vendor/tinno/hardware/interfaces/googlekey/aidl目录下生成我们刚刚创建的aidl文件,我感觉这是一个鸡肋,还不如我复制粘贴的快
3)编译问题
这里先介绍一个编译问题,我在实现好AIDL接口和AIDL HAL服务端之后,单编此模块报如下异常:
Environment variable DIST was modified (true => ), regenerating...
[ 60% 460/756] echo "API dump for the current version of AIDL interface vendor.tinno.hardware.oemsarsensor does not exist." && echo "Run the command \"m vendor.tinno.hardware.oemsarsensor-update-api\" or ad
FAILED: out/soong/.intermediates/vendor/tinno/hardware/interfaces/oemsarsensor/aidl/vendor.tinno.hardware.oemsarsensor_interface/checkapi_current.timestamp
echo "API dump for the current version of AIDL interface vendor.tinno.hardware.oemsarsensor does not exist." && echo "Run the command \"m vendor.tinno.hardware.oemsarsensor-update-api\" or add \"unstable: true\" to the build rule for the interface if it does not need to be versioned" && false # hash of input list: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
API dump for the current version of AIDL interface vendor.tinno.hardware.oemsarsensor does not exist.
Run the command "m vendor.tinno.hardware.oemsarsensor-update-api" or add "unstable: true" to the build rule for the interface if it does not need to be versioned
13:49:15 ninja failed with: exit status 1
报错原因:定义了 aidl_interface 但当前版本的 AIDL API dump(冻结的接口快照)不存在,Soong 的 checkapi_current 阶段就会直接失败。
并给出来两种解决方案:
- 执行命令m vendor.tinno.hardware.oemsarsensor-update-api生成稳定接口
- 配置unstable: true先绕过编译无需版本控制
因为这还是第一阶段,还需要各种调试,所以选择了方案二,在aidl_interface中配置了类似如下代码,此问题解决:
aidl_interface {
name: "vendor.tinno.hardware.oemsarsensor",
srcs: [
"vendor/tinno/hardware/oemsarsensor/Status.aidl",
"vendor/tinno/hardware/oemsarsensor/IOemSarSensor.aidl",
],
unstable: true,
backend: {
cpp: { enabled: true },
ndk: { enabled: true },
java: { enabled: true },
},
vendor_available: true,
}
2、AIDL HAL服务端实现
我们现在已经创建好了AIDL接口,同HIDL一样,现在可以创建hal服务端进程开始引用AIDL接口。同样参考IThermal模块,在aidl目录下面创建了default目录,并开始写Android.bp:
// Tinno AIDL OemSarSensor service (converted from HIDL)
cc_library_shared {
name: "vendor.tinno.hardware.oemsarsensor-impl",
relative_install_path: "hw",
vendor: true,
srcs: ["OemSarSensor.cpp"],
local_include_dirs: ["."],
shared_libs: [
"liblog",
"libbinder_ndk",
"vendor.tinno.hardware.oemsarsensor-ndk_platform",
],
}
cc_binary {
name: "vendor.tinno.hardware.oemsarsensor-service",
relative_install_path: "hw",
vendor: true,
srcs: [
"service.cpp",
"OemSarSensor.cpp",
],
local_include_dirs: ["."],
shared_libs: [
"liblog",
"libbase",
"libbinder_ndk",
"vendor.tinno.hardware.oemsarsensor-ndk_platform",
],
init_rc: ["vendor.tinno.hardware.oemsarsensor-service.rc"],
vintf_fragments: ["vendor.tinno.hardware.oemsarsensor-service.xml"],
}
PS:我这里其实是让AI Cursor帮我写的,这里的去链接了vendor.tinno.hardware.oemsarsensor-ndk_platform共享库和libbinder_ndk共享库
1)服务端进程主函数
//服务端进程启动配置文件vendor.tinno.hardware.oemsarsensor-service.rc
# Tinno AIDL OemSarSensor service (replaces HIDL vendor.tinno.hardware.oemsarsensor@1.0-service)
service oemsarsensor_aidl_service /vendor/bin/hw/vendor.tinno.hardware.oemsarsensor-service
class hal
user system
group system
//服务端进程入口文件service.cpp
// Tinno AIDL service main for IOemSarSensor (replaces HIDL service)
#define LOG_TAG "OemSarSensor-AIDL"
#include <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include "OemSarSensor.h"
using ::aidl::vendor::tinno::hardware::oemsarsensor::IOemSarSensor;
using ::aidl::vendor::tinno::hardware::oemsarsensor::OemSarSensor;
int main(int /* argc */, char* /* argv */ []) {
ABinderProcess_setThreadPoolMaxThreadCount(1);
//实例化AIDL接口对象
std::shared_ptr<IOemSarSensor> service = ndk::SharedRefBase::make<OemSarSensor>();
//AIDL服务端接口注册
std::string instance = std::string(IOemSarSensor::descriptor) + "/default";
binder_status_t status = AServiceManager_addService(service->asBinder().get(), instance.c_str());
if (status != STATUS_OK) {
LOG(ERROR) << "Could not register OemSarSensor AIDL service, status " << status;
return 1;
}
LOG(INFO) << "OemSarSensor AIDL service Ready (instance: " << instance << ").";
ABinderProcess_joinThreadPool();
LOG(INFO) << "OemSarSensor AIDL service Exit .";
return 0;
}
这里仿照IThermal服务端进程来编写的,通过AServiceManager_addService把AIDL接口服务实例,注册到servicemanager进程中。
2)IXXX接口功能实现
主函数实现了,接下来就是实现具体的OemSarSensor.h和OemSarSensor.cpp的代码了:
// OemSarSensor.h代码实现
#pragma once
#include <aidl/vendor/tinno/hardware/oemsarsensor/BnOemSarSensor.h>
#include <aidl/vendor/tinno/hardware/oemsarsensor/IOemSarSensor.h>
namespace aidl::vendor::tinno::hardware::oemsarsensor {
class OemSarSensor : public BnOemSarSensor {
public:
OemSarSensor();
ndk::ScopedAStatus setSarSensorMode(int32_t mode, Status* _aidl_return) override;
ndk::ScopedAStatus setVibraterEnable(int32_t mode, Status* _aidl_return) override;
ndk::ScopedAStatus setChargeLimit(int32_t limit, Status* _aidl_return) override;
ndk::ScopedAStatus setChargeNoLimit(int32_t limit, Status* _aidl_return) override;
};
} // namespace aidl::vendor::tinno::hardware::oemsarsensor
// OemSarSensor.cpp代码实现
#define LOG_TAG "OemSarSensor-AIDL"
#include "OemSarSensor.h"
#include <cerrno>
#include <cstring>
#include <fcntl.h>
#include <log/log.h>
#include <unistd.h>
namespace aidl::vendor::tinno::hardware::oemsarsensor {
OemSarSensor::OemSarSensor() {
ALOGE("OemSarSensor AIDL HAL start\n");
}
ndk::ScopedAStatus OemSarSensor::setSarSensorMode(int32_t mode, Status* _aidl_return) {
ALOGE("[=============%s, mode :%d=============]\n", __func__, mode);
write_int(P_SARSENSOR_FILE, mode);
*_aidl_return = Status::SUCCESS;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus OemSarSensor::setVibraterEnable(int32_t mode, Status* _aidl_return) {
ALOGE("[=============%s, mode :%d=============]\n", __func__, mode);
}
ndk::ScopedAStatus OemSarSensor::setChargeLimit(int32_t limit, Status* _aidl_return) {
ALOGE("[=============%s, limit :%d=============]\n", __func__, limit);
}
ndk::ScopedAStatus OemSarSensor::setChargeNoLimit(int32_t limit, Status* _aidl_return) {
ALOGE("[=============%s, limit :%d=============]\n", __func__, limit);
}
} // namespace aidl::vendor::tinno::hardware::oemsarsensor
如上整体思路其实和HIDL差不多,可以放心大胆的让AI Cursor来帮你写这份代码
3)编译问题
到这里服务端进程已经实现了,那么可以先单编看看有没有什么问题,除了前文介绍的AIDL interface does not exist之外,其实还报了一个异常:
[ 91% 253/276] //vendor/tinno/hardware/interfaces/oemsarsensor/aidl/default:vendor.tinno.hardware.oemsarsensor-impl link vendor.tinno.hardware.oemsarsensor-impl.so
FAILED: out/soong/.intermediates/vendor/tinno/hardware/interfaces/oemsarsensor/aidl/default/vendor.tinno.hardware.oemsarsensor-impl/android_vendor_arm64_armv8-a_kryo300_shared/unstripped/vendor.tinno.hardware.oemsarsensor-impl.so
echo "module vendor.tinno.hardware.oemsarsensor-impl missing dependencies: vendor.tinno.hardware.oemsarsensor-ndk_platform" && false
module vendor.tinno.hardware.oemsarsensor-impl missing dependencies: vendor.tinno.hardware.oemsarsensor-ndk_platform
报错原因:缺少vendor.tinno.hardware.oemsarsensor-ndk_platform库的依赖,我们来看看这个库是怎么回事?这个库貌似是AI给我写,参考IThermal服务端进程,使用的如下:

这个静态库的命名格式:aidl接口的包名-V版本号-ndk,看起来这个静态库是自动生成

最后咨询AI,把这个改成了vendor.tinno.hardware.oemsarsensor-ndk,最后编译通过了。
PS:为什么这里可以不用加-V版本号呢?是因为当前在在aidl_interface中并没有配置versions_with_info参数,因此当前无法生成带有版本号的ndk静态库。
3、AIDL HAL客户端实现
目前为止AIDL HAL服务端进程和接口实现的差不多了,我们一鼓作气把客户端的功能仿照IThermal的样式一起实现了,首先同HIDL一样,先引入java库:

如上截取的是调试之后的配置,因为这里没有对AIDL进行冻结,仿照前面ndk库的引用一样,这里直接配置的是vendor.tinno.hardware.oemsarsensor-java
1)客户端进程调用


2)兼容性矩阵配置
到此为止AIDL的客户端和服务端都实现了,我们编译刷机来运行一下,看看有什么问题,果不其然,第一个问题就是服务获取出来是空,如下报错日志:
03-19 10:42:53.641 892 892 I auditd : avc: denied { find } for pid=2263 uid=1000 name=vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
03-19 10:42:53.641 2263 2555 D WindowManager: waitForDeclaredService IOemSarSensor is NULL
日志调查,到目前为止selinux权限还没有配置,为了方便调试,我在init进程中强制设置了permissive模式,因此这个问题原因不应该是selinux权限导致的,检查AIDL HAL服务端进程是否成功起来:
01-16 10:41:54.930 1598 1598 E OemSarSensor-AIDL: OemSarSensor AIDL HAL start
01-16 10:41:54.938 892 892 I auditd : avc: denied { add } for pid=1598 uid=1000 name=vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default scontext=u:r:hal_oemsarsensor_aidl:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
01-16 10:41:54.943 1598 1598 W vendor.tinno.hardware.oemsarsensor-service: Thread Pool max thread count is 0. Cannot cache binder as linkToDeath cannot be implemented. serviceName: vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default
01-16 10:41:54.943 1598 1598 I OemSarSensor-AIDL: IOemSarSensor AIDL service Ready (instance: vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default).
AIDL HAL已经成功起来了,为什么客户端这里获取服务会直接是一个NULL呢?
跟踪Binder.allowBlocking(ServiceManager.waitForDeclaredService())代码发现了猫腻:


除了检查selinux权限,还会去检查vintf配置,因此我怀疑和兼容性矩阵配置有关系,于是在高通专用文件la.qssi16/vendor/qcom/opensource/core-utils/vendor_framework_compatibility_matrix.xml配置了FCM:
<compatibility-matrix version="1.0" type="framework">
//HIDL FCM的配置
<hal format="hidl" >
<name>vendor.tinno.hardware.oemsarsensor</name>
<transport>hwbinder</transport>
<version>1.0</version>
<interface>
<name>IOemSarSensor</name>
<instance>default</instance>
</interface>
</hal>
//AIDL FCM的配置
<hal format="aidl" optional="true">
<name>vendor.tinno.hardware.oemsarsensor</name>
<version>1</version>
<interface>
<name>IOemSarSensor</name>
<instance>default</instance>
</interface>
</hal>
</compatibility-matrix>
配置FCM之后问题依旧发现可能是设备清单的问题,我在手机上直接查找设备清单:

很奇怪设备清单文件编译进去了,但是内容是空的,结合AI的分析,原因是对VINTF进行了一系列检查,如果不符合要求就不会加载进去
因此AI把vendor.tinno.hardware.oemsarsensor-service.xml文件改成了如下:
<!-- Tinno AIDL OemSarSensor HAL (VINTF: use fqname for format="aidl" -->
<manifest version="1.0" type="device">
<hal format="aidl">
<name>vendor.tinno.hardware.oemsarsensor</name>
<version>1</version>
<fqname>IOemSarSensor/default</fqname>
</hal>
</manifest>
总结:关于兼容性矩阵和设备清单的配置,在google官方文档也有说明:

4、AIDL API冻结
在配置了兼容性矩阵和设备清单之后,重新编译验证,这次service已经不在是null了,但是运行时还是报了如下异常:
03-19 20:14:07.919 2147 2147 E BpBinder: Cannot do a user transaction on a vendor stability binder (vendor.tinno.hardware.oemsarsensor.IOemSarSensor) in a system stability context.
03-19 20:14:07.921 2147 2147 E Zygote : System zygote died with fatal exception
03-19 20:14:07.921 2147 2147 E Zygote : java.lang.RuntimeException: Error receiving broadcast Intent { act=android.hardware.usb.action.USB_STATE flg=0x31000010 xflg=0x4 (has extras) } in com.android.server.policy.PhoneWindowManager$21@863f0d4
03-19 20:14:07.921 2147 2147 E Zygote : at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1847)
03-19 20:14:07.921 2147 2147 E Zygote : at android.app.LoadedApk$ReceiverDispatcher$Args.$r8$lambda$mcNAAl1SQ4MyJPyDg8TJ2x2h0Rk(Unknown Source:0)
03-19 20:14:07.921 2147 2147 E Zygote : at android.app.LoadedApk$ReceiverDispatcher$Args$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.Handler.handleCallback(Handler.java:995)
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.Handler.dispatchMessage(Handler.java:103)
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.Looper.loopOnce(Looper.java:248)
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.Looper.loop(Looper.java:338)
03-19 20:14:07.921 2147 2147 E Zygote : at com.android.server.SystemServer.run(SystemServer.java:1051)
03-19 20:14:07.921 2147 2147 E Zygote : at com.android.server.SystemServer.main(SystemServer.java:704)
03-19 20:14:07.921 2147 2147 E Zygote : at java.lang.reflect.Method.invoke(Native Method)
03-19 20:14:07.921 2147 2147 E Zygote : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:593)
03-19 20:14:07.921 2147 2147 E Zygote : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:918)
03-19 20:14:07.921 2147 2147 E Zygote : Caused by: java.lang.IllegalArgumentException
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.BinderProxy.transactNative(Native Method)
03-19 20:14:07.921 2147 2147 E Zygote : at android.os.BinderProxy.transact(BinderProxy.java:592)
03-19 20:14:07.921 2147 2147 E Zygote : at vendor.tinno.hardware.oemsarsensor.IOemSarSensor$Stub$Proxy.setSarSensorMode(IOemSarSensor.java:152)
03-19 20:14:07.921 2147 2147 E Zygote : at com.android.server.policy.PhoneWindowManager$21.onReceive(PhoneWindowManager.java:7104)
03-19 20:14:07.921 2147 2147 E Zygote : at android.app.LoadedApk$ReceiverDispatcher$Args.lambda$getRunnable$0(LoadedApk.java:1839)
03-19 20:14:07.921 2147 2147 E Zygote : ... 11 more
03-19 20:14:07.921 2147 2147 D AndroidRuntime: Shutting down VM
最关键的日志是:
03-19 20:14:07.919 2147 2147 E BpBinder: Cannot do a user transaction on a vendor stability binder (vendor.tinno.hardware.oemsarsensor.IOemSarSensor) in a system stability context.
03-19 20:14:07.921 2147 2147 E Zygote : System zygote died with fatal exception
从日志来看不允许客户端调用不稳定的AIDL接口,因此我们接下来最后一件事情就是稳定AIDL接口,对API进行冻结。
1)更新AIDL API
回到之前报的API dump for the current version of AIDL interface does not exist编译报错的解决方案上,之前选择了配置unstable: true绕过了编译检查,因此采用如下步骤:
- 步骤一:去掉unstable: true配置
- 步骤二:执行命令m vendor.tinno.hardware.oemsarsensor-update-api生成current

- 结果:编译完成自动生成了aidl_api目录

2)冻结AIDL API
在更新AIDL API执行m vendor.tinno.hardware.oemsarsensor-update-api命令除了自动生成了aidl_api目录,还自动在aidl_interface中添加了frozen: false的配置,表示AIDL当前状态还没有进行冻结。
步骤一:保持frozen: false配置
步骤二:执行命令m vendor.tinno.hardware.oemsarsensor-freeze-api进行冻结

- 结果:此条命令执行成功之后,会自动在aidl_interface中添加版本相关配置,并生成aidl_api相关内容,还自动修改frozen: true的配置

- 注意事项:versions_with_info配置如上自动生成,不能手动添加,如果手动添加会报如下异常:

- 注意事项:在执行第二轮update-api和free-api之后,编译系统会自动增加版本:

3)稳定AIDL API
继冻结API之后,还需要做的事情就是稳定AIDL API,为什么要稳定?google官方文档也有相关说明:https://source.android.com/docs/core/architecture/aidl/stable-aidl?hl=zh-cn

通过如下几个步骤来稳定AIDL API:
- 步骤一:添加stability: "vintf"配置后进行单遍

- 步骤二:编译报错AIDL does not have VINTF level stability (marked @VintfStability)

关键日志是AIDL XXX does not have VINTF level stability (marked @VintfStability),即在声明了stability配置之后,AIDL接口缺少vintfStability标记,因此在接口上加上这类注解:

- 步骤三:服务端进程使用稳定版本的ndk库
服务端进程单编译报错missing dependencies: vendor.tinno.hardware.oemsarsensor-ndk

关键日志是AIDL HAL服务端进程缺少ndk依赖,为什么会缺少?因为前面冻结了AIDL API和版本,因此冻结之后的编译,只会生成带有版本的ndk,因此需要修改依赖:

- 步骤四:客户端进程也需要使用稳定版本的AIDL API

到目前为止,客户端进程和服务端进程都使用了稳定的AIDL接口,Cannot do a user transaction on a vendor stability binder问题被解决。
4)总结AIDL API
上述其实是我的调试过程,中间其实踩了一些坑,为了让这个流程更顺,方便下次直接AIDL接口开发,我这里进行了如下快速配置指南:
- 步骤一:配置aidl_interface
aidl_interface {
//unstable: true,
frozen: false,
stability: "vintf",
//千万不要手动配置versions_with_info
}
- 步骤二:执行命令m xxx-update-api更新API
- 步骤三:执行命令m xxx-freeze-api冻结API
- 步骤四:更新ndk库版本号/更新java库版本号
5、APP客户端实现
上面几节基本上打通了HAL AIDL的服务端和客户端进程。本节在介绍一下APP应用直接调用HAL AIDL服务端进程,即普通的三方应用直接去调用HAL AIDL。
相比HAL HIDL可要简单太多了,因为HAL AIDL的方式不需要去导入jar包,只需要去查找AIDL接口服务即可,我觉得这是AOSP最先进的设计。
1)APP直接创建AIDL接口
为什么不需要导入jar包了?因为AIDL本身就是为了提供跨进程服务的工具,并且AIDL已经广泛的在APP中进行了大量应用,即APP中操作HAL AIDL和FW AIDL的方式基本一致。
因此直接在APP工程中创建aidl文件,必须注意包名的根目录必须与上面服务端定义的一致不能有差别,并且接口名和里面的方法也必须一致。包名不一致会导致app检索不到对于的服务端AIDL 接口不一致会导致调用不到对应的方法。我们可以理解为这就是APP对AIDL接口的声明,用于在APP编译运行阶段会主动去创建对应的 接口,在调用时会主动进行匹配对应的AIDL Services 服务:

PS:APP客户端这边创建aidl文件的时候不能带有@VintfStability这些注解,这些注解是用来冻结 API声明稳定AIDL接口的,客户端不需要做这些事情。
2)APP配置启用AIDL接口
我们需要在buidl.gradle的编译文件中启用aidl资源包:

3)APP查找AIDL服务进程
接下来可以通过如下方式从service manager进程去查找获取对应的aidl服务端进程:

三、Selinux权限配置
AIDL HAL关于Selinux权限的配置,在Google官方文档中其实介绍的非常的详细,参考如下:
https://source.android.com/docs/core/architecture/aidl/aidl-hals?hl=zh-cn#sepolicy

public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
接下来以本案例中的oemsarsensor hal服务进程和fw systemserver作为客户端进程调用,详细解析selinux应该如何配置。
1、AIDL HAL服务进程配置
无论是AIDL HAL还是HIDL HAL他们其实都只是一种比较特殊的native进程,都是由init进程解析rc配置文件进行启动,因此都具有如下类似的selinux权限报错:
<14>[ 10.728014] .(5)[1:init]init 24: Parsing file /vendor/etc/init/vendor.tinno.hardware.oemsarsensor-service.rc...
<11>[ 17.617416] .(0)[1:init]init 34: [17448][0]Could not start service 'vendor.tinno.hardware.oemsarsensor-service' as part of class 'hal':
File /vendor/bin/hw/vendor.tinno.hardware.oemsarsensor-service(labeled "u:object_r:vendor_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.
Note: this error shows up even in permissive mode in order to make auditing denials possible.
前面已经讲了很多次了,即无法启动这个init rc 服务进程,原因是这个可执行bin文件的上下文标签没有进行定义。按照之前的流程,作如下配置:
1)AIDL HAL服务进程可执行文件的客体配置
- file_contexts文件中对可执行文件的客体上下文配置
# AIDL HAL服务进程可执行文件的客体配置
/(vendor|system/vendor)/bin/hw/vendor\.tinno\.hardware\.oemsarsensor-service u:object_r:hal_oemsarsensor_aidl_exec:s0
- hal_oemsarsensor_aidl.te文件中定义客体上下文
# 定义客体上下文hal_oemsarsensor_aidl_exec继承vendor_file_type和exec_type,表示是一个文件即客体
type hal_oemsarsensor_aidl_exec, exec_type, vendor_file_type, file_type;
2)AIDL HAL服务进程的主体配置
- hal_oemsarsensor_aidl.te文件中定义主体上下文
# 定义主体上下文hal_oemsarsensor_aidl继承domain,表示是一个进程域即主体
type hal_oemsarsensor_aidl, domain;
3)AIDL HAL服务进程进行域转换
type hal_oemsarsensor_aidl, domain;
type hal_oemsarsensor_aidl_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_oemsarsensor_aidl)
2、AIDL接口配置
经过第一节的配置,现在AIDL HAL服务进程终于可以起来,服务进程主函数在获取接口时候却崩溃了,发生了如下selinux权限问题:
01-01 00:14:22.663 901 901 I auditd : avc: denied { add } for pid=1591 uid=1000 name=vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default scontext=u:r:hal_oemsarsensor_aidl:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
如上avc日志表示aidl hal服务端进程(上下文hal_oemsarsensor_aidl)没有对客体资源vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default 的service_manager添加权限,因为这个客体资源的上下文是一个默认的default_android_service。

如上代码,aidl hal服务端进程启动之后,就去对IXXX接口进行操作,向service manager进程进行注册,同之前HIDL HAL和SystemService进程的服务接口注册原因一致,我们需要对AIDL接口赋予客体上下文。从前面的章节我们已经解释了AIDL这些接口为什么是客体资源,详细的可以查看本系列之前的内容。
因此本节主要是针对AIDL接口文件的selinux权限配置:
1)AIDL接口属性簇配置
- hal_oemsarsensor_aidl.te文件中批量定义AIDL接口相关的属性
# 定义几个属性: hal_oemsarsensor, hal_oemsarsensor_client, hal_oemsarsensor_server
hal_attribute(oemsarsensor)
此宏就是用来定义AIDL接口相关的属性,包括AIDL接口的属性、AIDL服务端的属性、AIDL客户端的属性。在system/sepolicy/public/te_macros代码中对齐进行了定义:
#####################################
# hal_attribute(hal_name)
# Add an attribute for hal implementations along with necessary
# restrictions.
define(`hal_attribute', `
attribute hal_$1;
expandattribute hal_$1 true;
attribute hal_$1_client;
expandattribute hal_$1_client true;
attribute hal_$1_server;
expandattribute hal_$1_server false;
neverallow { hal_$1_server -halserverdomain } domain:process fork;
# hal_*_client and halclientdomain attributes are always expanded for
# performance reasons. Neverallow rules targeting expanded attributes can not be
# verified by CTS since these attributes are already expanded by that time.
build_test_only(`
neverallow { hal_$1_server -hal_$1 } domain:process fork;
neverallow { hal_$1_client -halclientdomain } domain:process fork;
')
')
因此hal_attribute(oemsarsensor)展开之后如下:
# 定义了hal_oemsarsensor属性,代表接口?
attribute hal_oemsarsensor; expandattribute hal_oemsarsensor true;
# 定义了hal_oemsarsensor_client属性,代表客户端进程上下文
attribute hal_oemsarsensor_client; expandattribute hal_oemsarsensor_client true;
#定义了hal_oemsarsensor_server属性,代表服务端进程上下文
attribute hal_oemsarsensor_server; expandattribute hal_oemsarsensor_server false;
2)AIDL接口允许注册和查找
- hal_oemsarsensor_aidl.te文件中配置AIDL接口运行被注册和查找
# allow client to find the service, allow server to register the service
hal_attribute_service(hal_oemsarsensor, hal_oemsarsensor_service)
此宏就是用来配置AIDL接口允许被服务端进程注册,允许被客户端进程查找。在system/sepolicy/public/te_macros代码中对齐进行了定义:
###########################################
# hal_attribute_service(attribute, service)
# Ability for domain to get a service to service_manager
# and find it. It also creates a neverallow preventing
# others from adding it.
# Used to pair hal_foo_client with hal_foo_service
define(`hal_attribute_service', `
allow $1_client $2:service_manager find;
add_service($1_server, $2)
build_test_only(`
# if you are hitting this neverallow, try using:
# hal_client_domain(<your domain>, hal_<foo>)
# instead
neverallow {
domain
-$1_client
-$1_server
# some services are allowed to find all services
-atrace
# TODO(b/247267829) remove dumpstate
-dumpstate
-shell
-system_app
-traceur_app
} $2:service_manager find;
')
')
###########################################
# add_service(domain, service)
# Ability for domain to add a service to service_manager
# and find it. It also creates a neverallow preventing
# others from adding it.
define(`add_service', `
allow $1 $2:service_manager { add find };
neverallow { domain -$1 } $2:service_manager add;
# On debug builds with root, allow binder services to use binder over TCP.
# Not using rw_socket_perms_no_ioctl to avoid granting too many permissions.
userdebug_or_eng(`
allow $1 su:tcp_socket { accept getopt read write };
')
')
因此hal_attribute_service(hal_oemsarsensor, hal_oemsarsensor_service)展开之后如下内容:
#允许客户端进程对hal_oemsarsensor_service的查找
allow hal_oemsarsensor_client hal_oemsarsensor_service:service_manager find;
#允许服务端进程对hal_oemsarsensor_service的查找和添加
allow hal_oemsarsensor_server hal_oemsarsensor_service:service_manager { add find };
3)AIDL接口客体资源配置
那么hal_oemsarsensor_service是什么?其实就是AIDL接口的客体资源上下文。所以我们还需要对hal_oemsarsensor_service关联我们的接口资源,我这里的这个名字取得不是很好,它的本质就是客体资源,如下配置:
- service.te文件中对AIDL接口定义上下文
type hal_oemsarsensor_service, hal_service_type, protected_service, service_manager_type;
- service_contexts文件中对AIDL接口关联描述符
vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default u:object_r:hal_oemsarsensor_service:s0
这样是不是进行了完美的闭合?我这个案例中AIDL接口上下文的名字取得真陋!
然后根据这番操作,服务端进程可以通过service manager进程进行注册,客户端进程可以通过service manager进程查找这个服务。
疑问:AIDL接口注册在vndservice manager还是在service manager进程?
最初我把hal_oemsarsensor_service配置在vndservice.te和vndservice_contexts文件中的,然而配置了之后还是报不允许接口进行注册,后面结合AI如下内容:

后查看了IThermal AIDL配置,得出如下结论:即通过AServiceManager_addService函数进行注册,是向service manager进程进行注册。
3、关联AIDL服务端进程
上节已经通过hal_attribute的方式定义了服务端进程的属性hal_xxx_server,那么这个属性最后被指定给到哪个进程呢?然后这个进程允许具有binder通信功能吗?这个进程允许被客户端进程调用吗?这些能力其实也被封装到宏里面去了,在hal_oemsarsensor_aidl.te中进行如下配置:
# let this domain serve the hal service
hal_server_domain(hal_oemsarsensor_aidl, hal_oemsarsensor)
# allow binder connection from client to server
binder_call(hal_oemsarsensor_client, hal_oemsarsensor_server)
# allow binder communication from server to service_manager
binder_use(hal_oemsarsensor_server)
1)关联AIDL服务端进程
hal_server_domain就是用来完成这件事情的,其中第一个参数就是当前服务进程的上下文,第二个参数就是当前aidl接口簇的基本属性。在system/sepolicy/public/te_macros定义如下:
#####################################
# hal_server_domain(domain, hal_type)
# Allow a base set of permissions required for a domain to offer a
# HAL implementation of the specified type over HwBinder.
#
# For example, default implementation of Foo HAL:
# type hal_foo_default, domain;
# hal_server_domain(hal_foo_default, hal_foo)
#
define(`hal_server_domain', `
typeattribute $1 halserverdomain;
typeattribute $1 $2_server;
typeattribute $1 $2;
')
因此如上配置展开如下:
# hal_oemsarsensor_aidl 就是被指定的服务端进程上下文的属性
# hal_oemsarsensor_aidl 继承halserverdomain,表示是一个hal服务进程
typeattribute hal_oemsarsensor_aidl halserverdomain;
# hal_oemsarsensor_aidl 继承hal_oemsarsensor_server,对应当前AIDL服务端属性
typeattribute hal_oemsarsensor_aidl hal_oemsarsensor_server;#hal_oemsarsensor_aidl 继承hal_oemsarsensor,对应当前AIDL接口属性
typeattribute hal_oemsarsensor_aidl hal_oemsarsensor;
2)允许和service manager进程进行通信
binder_use就是用来允许AIDL服务端进程能够和service manager进程通过binder的方式进行通信,因为服务注册和查询的底层原理还是通过binder的方式进行跨进程通信,不清楚的同学可以看看本系列的前面几篇内容。在system/sepolicy/public/te_macros定义如下:
#####################################
# binder_use(domain)
# Allow domain to use Binder IPC.
define(`binder_use', `
# Call the servicemanager and transfer references to it.
allow $1 servicemanager:binder { call transfer };
# Allow servicemanager to send out callbacks
allow servicemanager $1:binder { call transfer };
# rw access to /dev/binder and /dev/ashmem is presently granted to
# all domains in domain.te.
')
因此如上配置展开如下:
# 允许hal_oemsarsensor_server进程可以对servicemanager进程的binder端点执行call/transfer操作
allow hal_oemsarsensor_server servicemanager:binder { call transfer };
# 允许servicemanager进程可以对hal_oemsarsensor_server进程的binder端点执行call/transfer操作
allow servicemanager hal_oemsarsensor_server:binder { call transfer };
3)允许和客户端进程进行通信
binder_call其实和binder_use类似,binder_use是去和service manager进程进行交互,而binder_call其实就是和指定进程进行交互,那么这里指定的进程通常就是客户端进程。在system/sepolicy/public/te_macros定义如下:
#####################################
# binder_call(clientdomain, serverdomain)
# Allow clientdomain to perform binder IPC to serverdomain.
define(`binder_call', `
# Call the server domain and optionally transfer references to it.
allow $1 $2:binder { call transfer };
# Allow the serverdomain to transfer references to the client on the reply.
allow $2 $1:binder transfer;
# Receive and use open files from the server.
allow $1 $2:fd use;
')
因此如上配置展开如下:
#允许客户端进程可以对服务端进程的binder端点执行call/transfer操作
allow hal_oemsarsensor_client hal_oemsarsensor_server:binder { call transfer };
#允许服务端进程可以对客户端进程的binder端点执行transfer操作
allow hal_oemsarsensor_server hal_oemsarsensor_client:binder transfer;
#允许客户端进程可以使用服务端进程的文件节点,这个目前还不知道有什么用?
allow hal_oemsarsensor_client hal_oemsarsensor_server:fd use;
4、关联AIDL客户端进程
最后我们来到客户端进程这边,这个案例是在systemserver进程中,监听usb插拔之后需要调用这个aidl hal服务,因此报了如下selinux权限问题:
03-22 08:02:16.376 901 901 I auditd : avc: denied { find } for pid=2322 uid=1000 name=vendor.tinno.hardware.oemsarsensor.IOemSarSensor/default scontext=u:r:system_server:s0 tcontext=u:object_r:default_android_service:s0 tclass=service_manager permissive=1
03-22 08:02:16.379 2322 2322 I auditd : type=1400 audit(0.0:5163): avc: denied { call } for comm="android.ui" scontext=u:r:system_server:s0 tcontext=u:r:hal_oemsarsensor_aidl:s0 tclass=binder permissive=1
如上第一行日志表示system_server进程没有对这个AIDL接口的查找权限,通过第二节的配置其实已经解决了这个问题,第二行日志表示system_server进程无法对这个AIDL接口执行call操作,其原因是system_server进程没有加入到AIDL客户端进程里面。同样在hal_oemsarsensor_aidl.te中进行如下配置:(PS 不一定非得在system_server.te中配置,因此编译后都要打包到一起,因此te里面的策略文件可以任意配置到不同文件中)
# let this domain use the hal service
hal_client_domain(system_server, hal_oemsarsensor)
binder_use(system_server)
1)关联AIDL客户端进程
hal_client_domain就是用来完成这件事情的,其中第一个参数就是客户端进程的上下文,第二个参数就是当前aidl接口簇的基本属性。在system/sepolicy/public/te_macros定义如下:
#####################################
# hal_client_domain(domain, hal_type)
# Allow a base set of permissions required for a domain to be a
# client of a HAL of the specified type.
#
# For example, make some_domain a client of Foo HAL:
# hal_client_domain(some_domain, hal_foo)
#
define(`hal_client_domain', `
typeattribute $1 halclientdomain;
typeattribute $1 $2_client;
# TODO(b/34170079): Make the inclusion of the rules below conditional also on
# non-Treble devices. For now, on non-Treble device, always grant clients of a
# HAL sufficient access to run the HAL in passthrough mode (i.e., in-process).
not_full_treble(`
typeattribute $1 $2;
# Find passthrough HAL implementations
allow $2 system_file:dir r_dir_perms;
allow $2 vendor_file:dir r_dir_perms;
allow $2 vendor_file:file { read open getattr execute map };
')
')
因此如上配置展开如下:
# 配置已有属性system_server继承halclientdomain
typeattribute system_server halclientdomain;
#配置已有属性system_server继承hal_oemsarsensor_client,即完全拥有此接口客户端进程所有能力
typeattribute system_server hal_oemsarsensor_client;
2)运行和service manager进程进行通信
和前面的一样了,这里的参数是客户端进程system_server,即允许system_server进程和service manager进程之间通过binder的方式进行通信。
写在最后
AIDL HAL 的迁移是 Android 14+ 开发者最大的痛点之一。
你在迁移过程中还遇到了哪些奇葩的 SELinux 报错?或者在 API 冻结时踩了什么坑?
欢迎在评论区留言,我们一起讨论解决!如果觉得本文对你有帮助,别忘了点赞收藏~
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)