前文已经介绍了硬件抽象层中的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_domainhal_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 冻结时踩了什么坑?
欢迎在评论区留言,我们一起讨论解决!如果觉得本文对你有帮助,别忘了点赞收藏~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐