Skip to content

AIDL服务实战

以 Android 17 Power AIDL HAL 为真实样本,追踪接口、NDK实现、服务注册、VINTF、客户端与测试的完整闭环。

基于android-17.0.0_r1
AndroidAIDLHALBinder

AIDL服务实战 ​

Power AIDL HAL 的可运行闭环跨越三个 owner:AIDL 构建生成接口,默认服务创建并注册 BnPower 实现,VINTF/VTS 再从设备侧发现并验证实例。下面的时序图把源码阅读顺序固定在这三层。

本文面向已经读过 AIDL与HAL迁移、Stable AIDL 和 AIDL接口版本管理 的读者。这里的“实战”不是创建一个脱离 AOSP 的玩具服务,而是沿 Android 17 hardware/interfaces/power/aidl 的真实实现,解释一项 AIDL HAL 如何从接口声明进入生成的 BnPower、服务进程、VINTF manifest,再被 VTS 找到和调用。

1. 接口入口 ​

源码文件:hardware/interfaces/power/aidl/android/hardware/power/IPower.aidl

java
@VintfStability
interface IPower {
    oneway void setMode(in Mode type, in boolean enabled);
    boolean isModeSupported(in Mode type);
    oneway void setBoost(in Boost type, in int durationMs);
    IPowerHintSession createHintSession(
            in int tgid, in int uid, in int[] threadIds, in long durationNanos);
}

接口同时定义了三种边界:oneway 方法没有 reply,查询方法返回布尔值,session 创建方法返回另一个 Binder 接口。@VintfStability 使它进入 Stable AIDL 的 structured、版本和 VINTF 校验链。

2. 构建产物 ​

源码文件:hardware/interfaces/power/aidl/Android.bp

make
aidl_interface {
    name: "android.hardware.power",
    vendor_available: true,
    stability: "vintf",
    backend: {
        cpp: { enabled: false },
        ndk: { enabled: true },
        java: { enabled: true, sdk_version: "module_current" },
        rust: { enabled: true },
    },
    frozen: true,
}

服务实现选择 NDK backend,因此源码 include 的不是 frameworks/native 的 CPP BnPower,而是生成的 aidl/android/hardware/power/BnPower.h。vendor_available 决定模块可以进入 vendor 侧依赖图,frozen 和 stability 则决定发布时的 API 门禁。

3. 实现对象 ​

源码文件:hardware/interfaces/power/aidl/default/Power.h

cpp
class Power : public BnPower {
  public:
    ndk::ScopedAStatus setMode(Mode type, bool enabled) override;
    ndk::ScopedAStatus isModeSupported(Mode type, bool* _aidl_return) override;
    ndk::ScopedAStatus createHintSession(
            int32_t tgid, int32_t uid, const std::vector<int32_t>& threadIds,
            int64_t durationNanos,
            std::shared_ptr<IPowerHintSession>* _aidl_return) override;
  private:
    std::vector<std::shared_ptr<IPowerHintSession>> mPowerHintSessions;
};

Power 是状态 owner;Binder 线程只负责把 Parcel 解码后的参数交给这些 override。返回值统一是 ScopedAStatus,业务结果通过 _aidl_return 或返回的 Binder 接口传出。

4. 方法实现 ​

源码文件:hardware/interfaces/power/aidl/default/Power.cpp

cpp
ScopedAStatus Power::isModeSupported(Mode type, bool* _aidl_return) {
    LOG(INFO) << "Power isModeSupported: " << static_cast<int32_t>(type);
    *_aidl_return = type >= MODE_RANGE.front() && type <= MODE_RANGE.back();
    return ScopedAStatus::ok();
}

ScopedAStatus Power::createHintSession(
        int32_t, int32_t, const std::vector<int32_t>& tids, int64_t,
        std::shared_ptr<IPowerHintSession>* _aidl_return) {
    if (tids.size() == 0) {
        *_aidl_return = nullptr;
        return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
    }
    auto session = ndk::SharedRefBase::make<PowerHintSession>();
    mPowerHintSessions.push_back(session);
    *_aidl_return = session;
    return ScopedAStatus::ok();
}

这里有两个可观察的失败边界:未知枚举值通过 isModeSupported 返回 false;空线程列表不是“创建空 session”,而是返回 EX_ILLEGAL_ARGUMENT。session 被加入 mPowerHintSessions 后由服务对象持有,避免只把 Binder 引用返回给客户端而丢失实现生命周期。

5. 服务注册 ​

源码文件:hardware/interfaces/power/aidl/default/main.cpp

cpp
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Power> power = ndk::SharedRefBase::make<Power>();
const std::string instance = std::string() + Power::descriptor + "/default";
binder_status_t status = AServiceManager_addService(
        power->asBinder().get(), instance.c_str());
CHECK_EQ(status, STATUS_OK);
ABinderProcess_joinThreadPool();

注册成功后,Power 的 Binder 对象由 service manager 持有,进程进入 NDK Binder 线程池。joinThreadPool 返回意味着服务进程即将退出,示例代码用 EXIT_FAILURE 表明这不是正常路径。

6. 启动清单 ​

源码文件:hardware/interfaces/power/aidl/default/power-default.rc、hardware/interfaces/power/aidl/default/power-default.xml

rc 文件负责 init 启动二进制;VINTF fragment 声明 android.hardware.power 的版本 7 和 IPower/default。三处必须一致:二进制注册的 descriptor/instance、rc 启动的服务、manifest 的 fqname。任一处拼写不一致,编译可能成功但客户端无法发现实例。

7. 客户端验证 ​

源码文件:hardware/interfaces/power/aidl/vts/VtsHalPowerTargetTest.cpp

cpp
AIBinder* binder = AServiceManager_waitForService(GetParam().c_str());
ASSERT_NE(binder, nullptr);
power = IPower::fromBinder(ndk::SpAIBinder(binder));
auto status = power->getInterfaceVersion(&mServiceVersion);
ASSERT_TRUE(status.isOk());
if (mServiceVersion >= 2) {
    status = power->createHintSession(getpid(), getuid(), kSelfTids,
                                      16666666L, &mSession);
    mSessionSupport = status.isOk();
}

测试先等待实例,再读取远端版本,最后按版本选择调用。mServiceVersion >= 2 是兼容路径:同一个测试二进制可以面对旧版本服务,而不是无条件调用新事务。

8. 能力与跳过 ​

VTS 对不支持的能力使用 EX_UNSUPPORTED_OPERATION 或 GTEST_SKIP,而不是把“硬件没有该功能”判成 Binder 故障。示例实现的 getCpuHeadroom/getGpuHeadroom 返回不支持;createHintSession 空线程列表则是参数错误,两者必须在排查时区分。

9. 调试路径 ​

当服务不可用时,按 owner 顺序检查:init 是否启动二进制;AServiceManager_addService 是否返回 STATUS_OK;注册字符串是否等于 android.hardware.power.IPower/default;manifest 是否声明同一 fqname 和版本;VTS 是否链接 android.hardware.power-V7-ndk。当服务可发现但调用失败,再进入 Power.cpp 的参数检查和 ScopedAStatus 返回值。

10. 阅读检查 ​

从 VtsHalPowerTargetTest::SetUp 反向追到 main.cpp,说明 Binder 引用如何从 service manager 到 IPower::fromBinder;再从 createHintSession 追到 PowerHintSession 的 owner 和失败返回。最后指出若把 mPowerHintSessions 删除,哪条生命周期保证会消失。能回答这三个问题,才完成了一次源码级 AIDL 服务阅读。