Android 自定义HAL开发篇之 HIDL篇——从入门到实战(中)

28 阅读16分钟

Android 自定义 HAL 开发篇之 HIDL 篇 —— 从入门到实战 (中)

这里给大家说个抱歉,由于上篇文章太大了,所以被迫分成三部分,这部分主要是将上一篇未完成的FW层适配做好。下一篇我主要是给大家讲一下在目前FW开发中,API SDK如何调用 Hide API 开放以及系统签名APP的制作。


第 6 章 完整版代码汇总

经过前四章的迭代,Hello HAL 已经是生产级的多客户端版本了。本章把所有最终代码汇总在一起,方便你整体对照或直接拷贝使用。

6.1 文件清单

vendor/custom/hardware/interfaces/
├── Android.bp                          # 包根声明
├── current.txt                         # 接口哈希
├── update-makefiles.sh                 # 生成脚本
│
├── hello_hidl/
│   └── 1.0/
│       ├── IHello.hal                  # 服务接口
│       ├── IHelloCallback.hal          # 回调接口
│       ├── types.hal                   # 自定义类型
│       ├── Android.bp                  # 自动生成的接口库
│       │
│       └── default/
|           └── test/
|               ├── Android.bp              # 测试编译规则
|               ├── hello_hidl_test.cpp     # 测试程序
│           ├── Android.bp              # 编译规则(纯绑定模式)
│           ├── Hello.h                 # 服务实现头文件
│           ├── Hello.cpp               # 服务实现(版本 D 完整版)
│           ├── HelloCallback.h         # 回调空桩头文件
│           ├── HelloCallback.cpp       # 回调空桩实现
│           ├── HelloService.cpp        # 服务主程序
│           ├── vendor.custom.hello_hidl@1.0-service.rc   # init 配置
│           └── vendor.custom.hello_hidl@1.0-service.xml  # VINTF manifest

6.2 接口定义(三个 .hal

IHello.hal

package vendor.custom.hello_hidl@1.0;

import IHelloCallback;

interface IHello {
    // 同步方法
    calculate_plus(uint32_t a, uint32_t b) generates (uint32_t total);
    write(string name) generates (uint32_t result);
    read() generates (string name);

    // 订阅方法
    subscribe(IHelloCallback callback) generates (StatusCode status);
    unsubscribe(IHelloCallback callback) generates (StatusCode status);
};

IHelloCallback.hal

package vendor.custom.hello_hidl@1.0;

interface IHelloCallback {
    oneway onValueSet(TemperatureAndPressure value);
    oneway onValueSetError(StatusCode errorCode);
};

types.hal

package vendor.custom.hello_hidl@1.0;

struct TemperatureAndPressure {
    uint64_t temperature;
    uint64_t pressure;
};

enum StatusCode : int32_t {
    OK = 0,
    ERROR = -1,
    INTERNAL_ERROR = -2,
};

6.3 服务实现(Hello.h + Hello.cpp

即第 5 章的版本 D 完整代码,此处不再重复粘贴,请直接参考第 5 章 5.4 节

6.4 回调空桩(HelloCallback.h + HelloCallback.cpp

即第 3 章 3.4 节的代码,服务端只保留空桩让 impl 库编译通过,真正的回调实现在客户端。

6.5 服务主程序(HelloService.cpp

即第 1 章 1.5 节的代码,不变。

6.6 编译配置(default/Android.bp

cc_defaults {
    name: "hello-hidl_defaults",
    relative_install_path: "hw",
    defaults: ["hidl_defaults"],
    vendor: true,
    proprietary: true,
    cflags: [
        "-Wno-unused-parameter",
        "-Wall",
    ],
    shared_libs: [
        "libcutils",
        "liblog",
        "libbase",
        "libsysutils",
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "vendor.custom.hello_hidl@1.0",
    ],
}

cc_library_static {
    name: "vendor.custom.hello_hidl@1.0-impl",
    defaults: ["hello-hidl_defaults"],
    srcs: [
        "Hello.cpp",
        "HelloCallback.cpp",
    ],
}

cc_binary {
    name: "vendor.custom.hello_hidl@1.0-service",
    defaults: ["hello-hidl_defaults"],
    init_rc: ["vendor.custom.hello_hidl@1.0-service.rc"],
    vintf_fragments: ["vendor.custom.hello_hidl@1.0-service.xml"],
    srcs: [
        "HelloService.cpp",
    ],
    static_libs: [
        "vendor.custom.hello_hidl@1.0-impl",
    ],
}

6.7 .rc.xml 以及测试程序

vendor.custom.hello_hidl@1.0-service.rc

service hello-hidl /vendor/bin/hw/vendor.custom.hello_hidl@1.0-service
    class hal
    user system
    group system

vendor.custom.hello_hidl@1.0-service.xml

<manifest version="1.0" type="device">
    <hal format="hidl">
        <name>vendor.custom.hello_hidl</name>
        <transport>hwbinder</transport>
        <version>1.0</version>
        <interface>
            <name>IHello</name>
            <instance>default</instance>
        </interface>
    </hal>
</manifest>

测试程序 defualt\test\Android.bp

//
// hello_hidl_test — IHello HAL 客户端测试程序
//
// 构建:  mm vendor/custom/hardware/interfaces/hello_hidl/1.0/default/test/hello_hidl_test
// 运行:  adb shell hello_hidl_test
//
// 前提:  vendor.custom.hello_hidl@1.0-service 已在设备上运行
//

cc_binary {
    name: "hello_hidl_test",

    // 安装到 vendor 分区,匹配 SELinux file_contexts
    vendor: true,
    proprietary: true,

    srcs: ["hello_hidl_test.cpp"],

    shared_libs: [
        "vendor.custom.hello_hidl@1.0",   // HAL 客户端接口库
        "libhidlbase",
        "libhidltransport",
        "libutils",
        "liblog",
    ],

    cflags: [
        "-Wall",
        "-Werror",
        "-Wno-unused-parameter",
    ],
}
    

defualt\test\hello_hidl_test.cpp

#include <vendor/custom/hello_hidl/1.0/IHello.h>
#include <vendor/custom/hello_hidl/1.0/IHelloCallback.h>
#include <vendor/custom/hello_hidl/1.0/types.h>

#include <hidl/Status.h>
#include <android/log.h>
#include <iostream>
#include <thread>
#include <chrono>
#include <atomic>

// ============================================================
// namespace 别名 — 测试文件没有嵌套 namespace,需要显式声明
// ============================================================
namespace V1_0 = ::vendor::custom::hello_hidl::V1_0;

using ::android::sp;
using ::android::hardware::hidl_string;
using ::android::hardware::Return;
using ::android::hardware::Void;

using IHello = V1_0::IHello;
using IHelloCallback = V1_0::IHelloCallback;
using TemperatureAndPressure = V1_0::TemperatureAndPressure;
using StatusCode = V1_0::StatusCode;

#define LOG_TAG "HelloHidlTest"
#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

// ============================================================
// 客户端 IHelloCallback 实现 — 用于接收 subscribe 推送
// ============================================================
struct TestCallback : public IHelloCallback {
    std::atomic<int> count{0};

    Return<void> onValueSet(const TemperatureAndPressure& value) override {
        int n = ++count;
        ALOGI("  [CB #%d] onValueSet: temperature=%llu, pressure=%llu",
              n,
              (unsigned long long)value.temperature,
              (unsigned long long)value.pressure);
        return Void();
    }

    Return<void> onValueSetError(StatusCode errorCode) override {
        ALOGE("  [CB] onValueSetError: code=%d", static_cast<int32_t>(errorCode));
        return Void();
    }
};

// ============================================================
// 主测试流程
// ============================================================
int main() {
    ALOGI("========== hello_hidl_test start ==========");

    // ----[1] 获取 HAL 服务----------------------------------------
    ALOGI("[1] Getting HAL service...");
    sp<IHello> service = IHello::getService();
    if (service == nullptr) {
        ALOGE("FAIL: Cannot get IHello service.");
        std::cerr << "FAIL: Cannot get IHello service." << std::endl;
        std::cerr << "Hint: make sure vendor.custom.hello_hidl@1.0-service is running." << std::endl;
        return 1;
    }
    ALOGI("PASS: Got HAL service.");

    // ----[2] 测试 calculate_plus ---------------------------------
    ALOGI("[2] Testing calculate_plus...");
    {
        struct {
            uint32_t a, b, expect;
        } cases[] = {{10, 20, 30}, {0, 0, 0}, {99999, 1, 100000}, {1, 2, 3}};
        for (auto& tc : cases) {
            uint32_t result = service->calculate_plus(tc.a, tc.b);
            if (result == tc.expect) {
                ALOGI("  PASS: %u + %u = %u", tc.a, tc.b, result);
            } else {
                ALOGE("  FAIL: %u + %u expected %u, got %u", tc.a, tc.b, tc.expect, result);
            }
        }
    }

    // ----[3] 测试 write / read  ----------------------------------
    ALOGI("[3] Testing write/read...");
    {
        const char* input = "Hello from test client!";
        uint32_t rc = service->write(input);
        ALOGI("  write(\"%s\") returned %u", input, rc);

        service->read([](const hidl_string& name) {
            bool ok = (name == "Hello from test client!");
            if (ok)
                ALOGI("  PASS: read() = \"%s\" — matches write", name.c_str());
            else
                ALOGE("  FAIL: read() = \"%s\" — expected \"Hello from test client!\"", name.c_str());
        });

        // 验证覆盖
        service->write("overwrite test");
        service->read([](const hidl_string& name) {
            bool ok = (name == "overwrite test");
            if (ok)
                ALOGI("  PASS: overwrite then read = \"%s\"", name.c_str());
            else
                ALOGE("  FAIL: overwrite read = \"%s\"", name.c_str());
        });
    }

    // ----[4] 测试 subscribe / unsubscribe ------------------------
    ALOGI("[4] Testing subscribe (wait 5s, expect ~5 callbacks)...");
    {
        sp<TestCallback> cb = new TestCallback();

        StatusCode st = service->subscribe(cb);
        if (st == StatusCode::OK)
            ALOGI("  subscribe() OK, sleeping 5 seconds...");
        else
            ALOGE("  FAIL: subscribe() returned %d", static_cast<int32_t>(st));

        std::this_thread::sleep_for(std::chrono::seconds(5));

        int n = cb->count.load();
        ALOGI("  Received %d callbacks in 5 seconds", n);
        if (n >= 4 && n <= 6)
            ALOGI("  PASS: callback rate ~1 Hz");
        else
            ALOGE("  FAIL: expected ~5 callbacks, got %d", n);

        // 取消订阅
        st = service->unsubscribe(cb);
        if (st == StatusCode::OK)
            ALOGI("  unsubscribe() OK");
        else
            ALOGE("  FAIL: unsubscribe() returned %d", static_cast<int32_t>(st));

        // 确认停止推送
        int before = cb->count.load();
        std::this_thread::sleep_for(std::chrono::seconds(2));
        int after = cb->count.load();
        if (before == after)
            ALOGI("  PASS: no callbacks after unsubscribe");
        else
            ALOGE("  FAIL: %d extra callbacks received after unsubscribe", after - before);
    }

    // ----[5] 测试订阅排他性 --------------------------------------
    ALOGI("[5] Testing subscribe exclusivity...");
    {
        sp<TestCallback> cb1 = new TestCallback();
        sp<TestCallback> cb2 = new TestCallback();

        StatusCode s1 = service->subscribe(cb1);
        ALOGI("  First subscribe: %s", s1 == StatusCode::OK ? "OK" : "ERROR");

        StatusCode s2 = service->subscribe(cb2);
        if (s2 == StatusCode::ERROR)
            ALOGI("  PASS: second subscribe correctly rejected");
        else
            ALOGE("  FAIL: second subscribe should have been rejected");

        service->unsubscribe(cb1);
    }

    ALOGI("========== hello_hidl_test done ==========");
    std::cout << "All tests completed!" << std::endl;
    return 0;
}
    

本章小结

到这里,Hello HAL 的 C++ 侧全部就绪:接口定义、服务实现、回调空桩、主程序、编译配置、init/VINTF 配置一应俱全。下一章讲怎么从 Java 侧调用它。


📝 下一章第 7 章 Java / Native 调用完整示例


第 7 章 Java / Native 调用完整示例

本章目标:从 Java App 侧完整调用 Hello HAL 的所有接口——同步调用、异步 read 回调、subscribe 订阅推送、退出时 unsubscribe。

7.1 添加编译依赖

在你的 App Framework 模块的 Android.bp 中引入 HIDL 生成的 Java 绑定:

android_app {
    name: "HelloHidlDemo",
    ...
    static_libs: [
        "vendor.custom.hello_hidl-V1.0-java",  // HIDL 生成的 Java 绑定
    ],
}

如果是老式 Android.mk

LOCAL_STATIC_JAVA_LIBRARIES += vendor.custom.hello_hidl-V1.0-java

或者是Gradle:


implementation files('libs\vendor.custom.hello_hidl-V1.0-java.jar')

这个 jar 包就是第 1 章 1.12 节说的 combined/ 目录下的完整产物。

7.2 导入类

import vendor.custom.hello_hidl.V1_0.IHello;
import vendor.custom.hello_hidl.V1_0.IHelloCallback;
import vendor.custom.hello_hidl.V1_0.TemperatureAndPressure;
import vendor.custom.hello_hidl.V1_0.StatusCode;

import android.os.HwBinder;
import android.os.RemoteException;
import java.util.NoSuchElementException;

7.3 获取 HAL 服务

IHello service;

try {
    // getService() 是阻塞调用,等待 hwservicemanager 返回服务
    service = IHello.getService();
} catch (NoSuchElementException e) {
    Log.e(TAG, "HAL service not found", e);
    return;
} catch (RemoteException e) {
    Log.e(TAG, "Binder error", e);
    return;
}

getService() 找不到服务会抛 NoSuchElementException,binder 异常抛 RemoteException,都要 catch。

7.4 同步调用:calculate_plus / write

// 加法 — 直接返回值
try {
    int result = service.calculate_plus(10, 20);
    Log.i(TAG, "10 + 20 = " + result);  // → 30
} catch (RemoteException e) {
    Log.e(TAG, "calculate_plus failed", e);
}

// 写入字符串 — 直接返回值
try {
    int rc = service.write("Hello from Java!");
    Log.i(TAG, "write returned: " + rc);  // → 0 表示成功
} catch (RemoteException e) {
    Log.e(TAG, "write failed", e);
}

7.5 异步回调:read

read().hal 里定义的是 generates (string name),Java 侧会生成一个 callback 参数:

try {
    service.read((int status, String name) -> {
        Log.i(TAG, "read() returned: " + name);  // → "Hello from Java!"
    });
} catch (RemoteException e) {
    Log.e(TAG, "read failed", e);
}

注意这里的 callback 是 HIDL 为"返回值"生成的同步回调,和第 3 章的 IHelloCallback 异步推送回调不是一回事。

7.6 订阅推送:subscribe + IHelloCallback 实现

这是最关键的部分。IHelloCallback 是一个 HIDL 回调接口,客户端需要实现它并通过 subscribe() 传给服务端:

// ===== 自定义 IHelloCallback 实现 =====
class MyHelloCallback extends IHelloCallback.Stub {

    private final String mTag;

    MyHelloCallback(String tag) {
        mTag = tag;
    }

    @Override
    public void onValueSet(TemperatureAndPressure value) {
        // 这个方法每秒被 HAL 服务回调一次(oneway)
        Log.i(mTag, "onValueSet: temperature=" + value.temperature
                + ", pressure=" + value.pressure);
        // value.temperature 和 value.pressure 每次递增 1
    }

    @Override
    public void onValueSetError(int errorCode) {
        Log.e(mTag, "onValueSetError: " + errorCode);
    }
}

// ===== 使用 =====
MyHelloCallback callback = new MyHelloCallback(TAG);

try {
    // 订阅
    int status = service.subscribe(callback);
    if (status == StatusCode.OK) {
        Log.i(TAG, "subscribed, waiting for callbacks...");
        // 之后 MyHelloCallback.onValueSet() 每秒被调用一次
        // 推送的值:t=1s (0,0), t=2s (1,1), t=3s (2,2) ...
    }
} catch (RemoteException e) {
    Log.e(TAG, "subscribe failed", e);
}

// ... 一段时间后取消订阅 ...
try {
    int status = service.unsubscribe(callback);
    if (status == StatusCode.OK) {
        Log.i(TAG, "unsubscribed");
    }
} catch (RemoteException e) {
    Log.e(TAG, "unsubscribe failed", e);
}

7.7 完整示例:一个 Activity 跑通全部 API

public class HelloHidlActivity extends Activity {

    private static final String TAG = "HelloHidlDemo";

    private IHello mService;
    private MyHelloCallback mCallback;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 1. 获取服务
        try {
            mService = IHello.getService();
        } catch (Exception e) {
            Log.e(TAG, "Failed to get service", e);
            return;
        }

        // 2. calculate_plus
        try {
            Log.i(TAG, "10 + 20 = " + mService.calculate_plus(10, 20));
        } catch (RemoteException e) { /* handle */ }

        // 3. write → read 验证
        try {
            mService.write("Java test");
            mService.read((s, name) ->
                Log.i(TAG, "read: " + name));  // → "Java test"
        } catch (RemoteException e) { /* handle */ }

        // 4. subscribe(每秒推送一次递增的值)
        mCallback = new MyHelloCallback(TAG);
        try {
            mService.subscribe(mCallback);
        } catch (RemoteException e) { /* handle */ }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 取消订阅 —— 重要!虽然服务端有 linkToDeath 兜底,
        // 但主动 unsubscribe 是好习惯
        if (mService != null && mCallback != null) {
            try {
                mService.unsubscribe(mCallback);
            } catch (RemoteException e) { /* handle */ }
        }
    }

    // ===== 内部类:回调实现 =====
    static class MyHelloCallback extends IHelloCallback.Stub {
        private final String tag;

        MyHelloCallback(String t) { tag = t; }

        @Override
        public void onValueSet(TemperatureAndPressure v) {
            Log.i(tag, "t=" + v.temperature + " p=" + v.pressure);
        }

        @Override
        public void onValueSetError(int code) {
            Log.e(tag, "error: " + code);
        }
    }
}

💡 即使你不调 unsubscribe,第 4-5 章的 linkToDeath 机制也会在 App 退出时自动清理订阅。但主动 unsubscribe 是好习惯——能让服务端立刻释放资源,不用等死亡通知。

但是,其实在现代 Android(Project Treble 引入之后)的安全架构中,App 是被严格禁止直接调用 Vendor HAL 服务的。

系统底层的 SELinux 规则(具体来说是 untrusted_app 域的规则)会直接拦截 App 对 hwservicemanager 以及各种 Vendor 域服务的访问。

🌟 Android 跨分区调用的“黄金路径”

所以,在实际的 Android 系统开发中,合规的调用关系通常只有以下几种:

1. 黄金准则:App -> System -> Vendor

这是 95% 以上硬件功能的标准调用链路,也就是你提到的“中间商赚差价”模式。

  • App (普通应用) :调用标准的 Java API(如 CameraManager)。
  • System (系统服务) :位于 /systemCameraService 接收请求,进行权限校验(验证 App 是否申请了相机权限)。校验通过后,作为“代理人”跨越边界。
  • Vendor (硬件服务) :位于 /vendor 的 Camera HAL 收到 CameraService 的指令,去操作真正的 /dev/video0 节点。

2. 系统内部流转:System -> Vendor

这通常是系统自发的行为,不需要 App 触发。

  • 例如:系统位于 /system 的耗电监控服务(BatteryStatsService),每隔几分钟自动通过 Binder 去调用 /vendor 下的 Health HAL,读取底层电池节点的电压和温度。

3. 纯软件服务:App -> System

这是没有底层硬件参与的纯上层逻辑。

  • 例如:App 调用 ActivityManagerService 来启动另一个页面,或者调用 logd 来写入日志。全都在只读的 System 分区内的内存中进行,不涉及底层节点。

(注:极其特殊的例外是 NDK 开发中的图形渲染等对性能要求极致的场景,App 的进程会通过特定的直通模式直接加载 Vendor 下的 OpenGL/Vulkan 驱动 .so。)

下一章我们进行最正统的方式进行开发


本章小结

第 7 章演示了从 Java 侧调用 Hello HAL 的完整流程:

  • IHello.getService() 获取服务
  • calculate_plus / write 同步调用
  • read 的同步回调用法
  • IHelloCallback.Stub 实现订阅推送
  • 完整 Activity 示例,onDestroy 里主动 unsubscribe

📝 下一章第 8 章 Framework 层适配


第 8 章 Framework 层适配

本章目标:在 Framework 层封装一个 HelloManager,让 App 通过 Context.getSystemService() 调用 Hello HAL——而不是直接拿 HIDL 接口。这是 Android 系统服务的标准做法。

image.png

8.1 为什么要 Framework 层适配

直接用 IHello.getService() 也能调 HAL,但有几个问题:

  1. App 权限:普通 App 没有 find hwservice 的 SELinux 权限
  2. 生命周期:HAL 连接失败时谁来做重连?App 侧写太分散
  3. API 规范:HIDL 的 calculate_plus 这种命名不符合 Java 规范,需要转成 calculatePlus

标准做法是走 system_server 路线:App → AIDL → system_server 里的 HelloService → HIDL → HAL。

┌──────────┐   AIDL Binder   ┌──────────────┐   HIDL hwbinder   ┌─────────────┐
│  App     │ ──────────────▶ │ system_server│ ────────────────▶ │ Hello HAL   │
│          │                 │              │                   │ (vendor进程) │
│ HelloManager│              │ HelloService │                   │             │
│ (Context)  │                │ (SystemService│                  │ IHello.hal  │
│          │                 │  + Watchdog) │                   │             │
└──────────┘                 └──────────────┘                   └─────────────┘
        AIDL 接口                      HIDL Java 绑定

8.2 文件清单

新增 4 个文件 + 修改 4 个现有文件:

文件路径作用
IHelloCallback.aidlframeworks/base/core/java/android/hello/Framework 层回调接口
IHelloService.aidl同上Framework 层服务接口
HelloManager.java同上对 App 暴露的 Manager
HelloService.javaframeworks/base/services/core/java/com/android/server/hello/system_server 里的服务实现
Context.java(修改)frameworks/base/core/java/android/content/加服务名常量
SystemServer.java(修改)frameworks/base/services/java/com/android/server/注册服务
SystemServerRegistry.java(修改)base/core/java/android/app/SystemServiceRegistry.java注册服务
Android.bp(修改)base/services/core/Android.bp添加引用Jar包

8.3 定义 AIDL 接口

IHelloCallback.aidl

package android.hello;

/**
 * 客户端回调 — 对应 HAL 的 IHelloCallback.hal
 * oneway = 异步,不阻塞 HAL 推送线程
 */
oneway interface IHelloCallback {
    void onValueSet(long temperature, long pressure);
    void onValueSetError(int errorCode);
}

IHelloService.aidl

package android.hello;

import android.hello.IHelloCallback;

/**
 * Framework Hello 服务接口 — 映射 IHello.hal 全部方法
 */
interface IHelloService {
    int calculatePlus(int a, int b);
    int write(String name);
    String read();
    int subscribe(IHelloCallback callback);
    int unsubscribe(IHelloCallback callback);
}

AIDL 方法名用驼峰(calculatePlus),内部转调 HIDL 的下划线命名(calculate_plus)——这就是 Framework 层的"翻译"职责。

8.4 实现 HelloService.java

HelloService.java

package com.android.server.hello;

import android.content.Context;
import android.hello.IHelloService;
import android.hello.IHelloCallback;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import com.android.server.SystemService;
import com.android.server.Watchdog;

import vendor.custom.hello_hidl.V1_0.IHello;
import vendor.custom.hello_hidl.V1_0.StatusCode;
import vendor.custom.hello_hidl.V1_0.TemperatureAndPressure;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * HelloService — AIDL ↔ HIDL 桥接,方案 A(纯 Java HIDL bindings)。
 *
 * 继承 SystemService → Lifecycle(onStart / onBootPhase)
 * BinderService 实现 Watchdog.Monitor → Watchdog 定期心跳检测
 */
public class HelloService extends SystemService {
    private static final String TAG = "HelloService";

    // HAL 客户端接口(通过 gen_java 生成的 Java bindings)
    private IHello mHal;

    // 多客户端回调映射:AIDL callback binder → HIDL callback wrapper
    private final Map<IBinder, HwCallbackWrapper> mCallbacks = new ConcurrentHashMap<>();

    public HelloService(Context context) {
        super(context);
    }

    // ==================== Lifecycle ====================

    @Override
    public void onStart() {
        Log.i(TAG, "onStart: publishing binder service");
        publishBinderService(Context.HELLO_SERVICE, new BinderService());
    }

    @Override
    public void onBootPhase(int phase) {
        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {

        }
        if (phase == PHASE_BOOT_COMPLETED) {
            Log.e(TAG, "onBootCompleted");
            try {
                mHal = IHello.getService(true /* retry */);
                Log.i(TAG, "onBootPhase: HAL connected");
            } catch (Exception e) {
                Log.e(TAG, "onBootPhase: HAL connect failed", e);
            }
        }
    }

    // ==================== Binder Service(inner class)====================

    private final class BinderService extends IHelloService.Stub
                                       implements Watchdog.Monitor {

        // ---- Watchdog.Monitor ----
        @Override
        public void monitor() {
            if (mHal == null) {
                Watchdog.getInstance().setAllowRestart(false);
            }
        }

        // ---- calculatePlus ----
        @Override
        public int calculatePlus(int a, int b) {
            try {
                return mHal.calculate_plus(a, b);
            } catch (RemoteException e) {
                Log.e(TAG, "calculatePlus failed", e);
                return -1;
            }
        }

        // ---- write ----
        @Override
        public int write(String name) {
            try {
                return mHal.write(name);
            } catch (RemoteException e) {
                Log.e(TAG, "write failed", e);
                return -1;
            }
        }

        // ---- read (HIDL generates 同步返回) ----
        @Override
        public String read() {
            try {
                return mHal.read();
            } catch (RemoteException e) {
                Log.e(TAG, "read failed", e);
                return "";
            }
        }

        // ---- subscribe ----
        @Override
        public int subscribe(IHelloCallback aidlCb) {
            if (aidlCb == null) return StatusCode.ERROR;

            // 构造 HIDL callback wrapper
            HwCallbackWrapper wrapper = new HwCallbackWrapper(aidlCb);

            try {
                int rc = mHal.subscribe(wrapper);
                if (rc == StatusCode.OK) {
                    mCallbacks.put(aidlCb.asBinder(), wrapper);

                    // 注册 AIDL callback 死亡通知 — App 挂了自动清理
                    aidlCb.asBinder().linkToDeath(new IBinder.DeathRecipient() {
                        @Override
                        public void binderDied() {
                            Log.i(TAG, "App died, auto-unsubscribing");
                            mCallbacks.remove(aidlCb.asBinder());
                        }
                    }, 0);
                }
                return rc;
            } catch (RemoteException e) {
                Log.e(TAG, "subscribe failed", e);
                return StatusCode.ERROR;
            }
        }

        // ---- unsubscribe ----
        @Override
        public int unsubscribe(IHelloCallback aidlCb) {
            if (aidlCb == null) return StatusCode.ERROR;

            HwCallbackWrapper wrapper = mCallbacks.remove(aidlCb.asBinder());
            if (wrapper == null) {
                Log.w(TAG, "unsubscribe: no matching subscription");
                return StatusCode.ERROR;
            }

            // 解绑死亡通知
            aidlCb.asBinder().unlinkToDeath(null, 0);

            try {
                return mHal.unsubscribe(wrapper);
            } catch (RemoteException e) {
                Log.e(TAG, "unsubscribe failed", e);
                return StatusCode.ERROR;
            }
        }
    }

    // ============================================================
    // AIDL IHelloCallback → HIDL IHelloCallback 适配器
    // 将 App 侧的 AIDL 回调转换为 HAL 能识别的 HIDL 回调
    // ============================================================
    static class HwCallbackWrapper extends vendor.custom.hello_hidl.V1_0.IHelloCallback.Stub {
        private final IHelloCallback mAidlCallback;

        HwCallbackWrapper(IHelloCallback cb) {
            mAidlCallback = cb;
        }

        @Override
        public void onValueSet(TemperatureAndPressure value) {
            try {
                mAidlCallback.onValueSet(value.temperature, value.pressure);
            } catch (RemoteException e) {
                Log.e(TAG, "forward onValueSet failed", e);
            }
        }

        @Override
        public void onValueSetError(int errorCode) {
            try {
                mAidlCallback.onValueSetError(errorCode);
            } catch (RemoteException e) {
                Log.e(TAG, "forward onValueSetError failed", e);
            }
        }
    }
}

关键设计点:

  • HwCallbackWrapper:实现了 HIDL 的 IHelloCallback.Stub,内部持有 App 传来的 AIDL callback。HAL 推送数据时调 wrapper 的 onValueSet,wrapper 转发给 App——这就是"AIDL ↔ HIDL 桥接"。
  • linkToDeath:App 退出时自动 unsubscribe,和 HAL 侧的 linkToDeath 形成双重保险。
  • Watchdog.Monitor:定期检查 HAL 连接是否正常。

8.5 实现 HelloManager.java

HelloManager.java

package android.hello;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.content.Context;
import android.os.ServiceManager.ServiceNotFoundException;

import java.util.concurrent.Executor;


public final class HelloManager {
    private static final String TAG = "HelloManager";


    private  IHelloService sService;

    private final Context mContext;

    /**
     * @hide
     */
    public HelloManager(Context context) throws ServiceNotFoundException {
        mContext = context;
        sService = IHelloService.Stub.asInterface(
                ServiceManager.getServiceOrThrow(Context.HELLO_SERVICE));
    }

    // ================================================================
    // 同步方法
    // ================================================================
    /**
     * @hide
     */
    public int calculatePlus(int a, int b) {
        try {
            return sService.calculatePlus(a, b);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (Throwable t) {
            Log.e(TAG, "calculatePlus error", t);
        }
        return 0;
    }
    /**
     * @hide
     */
    public int write( String name) {
        try {
            return sService.write(name);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (Throwable t) {
            Log.e(TAG, "write error", t);
        }
        return 0;
    }

    /**
     * @hide
     */
    public String read() {
        try {
            return sService.read();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (Throwable t) {
            Log.e(TAG, "read error", t);
        }
        return "";
    }

    // ================================================================
    // 订阅方法
    // ================================================================

    /**
     * @hide
     */
    public int subscribe(@NonNull IHelloCallback callback) {
        try {
            return sService.subscribe(callback);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * @hide
     */
    public int subscribe(@NonNull IHelloCallback callback, @NonNull Executor executor) {
        return subscribe(callback);
    }

    /**
     * @hide
     */
    public int unsubscribe(@NonNull IHelloCallback callback) {
        try {
            return sService.unsubscribe(callback);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return 0;
    }

    /**
     * @hide
     */
    public int unsubscribe(@NonNull IHelloCallback callback, @NonNull Executor executor) {
        return unsubscribe(callback);
    }
}

8.6 修改 Context.java

添加服务名常量:

// frameworks/base/core/java/android/content/Context.java
public static final String HELLO_SERVICE = "hello";

8.7 修改 SystemServer.java

startOtherServices() 方法中注册:

// frameworks/base/services/java/com/android/server/SystemServer.java
import com.android.server.hello.HelloService;

// 在 startOtherServices() 内合适位置:
// Custom Hello HAL bridge
t.traceBegin("StartHelloService");
mSystemServiceManager.startService(HelloService.class);
t.traceEnd();

8.8 添加 HIDL jar 到 services

HelloService.java 用到了 HIDL Java 绑定,要在 services/core/Android.bp 加依赖:

// frameworks/base/services/core/Android.bp
 java_library_static {
     ...
     "com.android.sysprop.watchdog",
+    "vendor.custom.hello_hidl-V1.0-java",
 ],

8.9 SystemServiceRegistry 注册

// base/core/java/android/app/SystemServiceRegistry.java
// Not instantiable.
    private SystemServiceRegistry() { }

    static {
        registerService(Context.HELLO_SERVICE, HelloManager.class,
                new CachedServiceFetcher<HelloManager>() {
                    @Override
                    public HelloManager createService(ContextImpl ctx)
                            throws ServiceNotFoundException {
                        return new HelloManager(ctx.getOuterContext());
                    }
                });
}

8.10 编译验证

# 更新 API(因为有新增 @hide 接口)
make update-api -j32

# 测试编译一下
make servcies -j32

8.11 App 侧使用

Framework 适配完成后,App 侧就不再是直接调 HIDL,而是通过 Context 拿 Manager:

// 不再需要 IHello.getService(),改用:
HelloManager hm = (HelloManager) context.getSystemService(Context.HELLO_SERVICE);

// 驼峰命名,符合 Java 规范
int result = hm.calculatePlus(10, 20);
hm.write("from app");
String val = hm.read();

对比第 7 章的直接调用,Framework 适配后的 API 更规范、权限更受控、生命周期由 system_server 统一管理。


本章小结

第 8 章完成了 Framework 层适配:

  • AIDL 接口(IHelloService / IHelloCallback)作为 App 和 system_server 的契约
  • HelloService 在 system_server 里桥接 AIDL ↔ HIDL
  • HwCallbackWrapper 做 callback 适配
  • HelloManager 对 App 暴露规范 API
  • Context.java / SystemServer.java 注册服务
  • App 侧从 getSystemService() 获取 Manager

📝 下一章第 9 章 收尾:总结、踩坑清单与后续


第 9 章 收尾:总结、踩坑清单与后续

9.1 全流程总结

经过九章的迭代,我们从零搭建了一个完整的自定义 HIDL HAL。回顾整个端到端调用链:

image.png

┌─────────────────────────────────────────────────────────────────┐
│                        端到端调用链                               │
│                                                                 │
│  ┌───────┐  AIDL   ┌──────────────┐  HIDL   ┌───────────────┐  │
│  │  App  │────────▶│ system_server│────────▶│  Hello HAL    │  │
│  │       │ Binder  │              │ hwbinder│  (vendor进程)  │  │
│  │Hello  │         │ HelloService │         │               │  │
│  │Manager│         │ (AIDL↔HIDL   │         │ IHello.hal    │  │
│  │       │         │  桥接)       │         │ Hello.cpp     │  │
│  └───────┘         └──────────────┘         └───────────────┘  │
│     │                    │                        │            │
│     │ subscribe(cb)      │ subscribe(hwCb)        │ map<pid>   │
│     │◀── onValueSet ─────│◀── onValueSet ─────────│ 推送循环    │
│     │                    │                        │ linkToDeath │
│                                                                 │
│  SELinux: file_contexts + hello_hidl.te + hwservice_contexts   │
└─────────────────────────────────────────────────────────────────┘

四阶段迭代回顾:

阶段版本核心问题解决方案
① 基础版A怎么让 HAL 跑起来.hal + hidl-gen + service.cpp + rc/xml
② 踩坑版BApp 退出 HAL 崩溃(故意不解决,暴露问题)
③ 修复版C悬空 callback 导致 SIGABRTlinkToDeath 两条防线
④ 进阶版D多客户端订阅覆盖map<pid_t> 按 PID 隔离

9.2 踩坑清单

整理本文涉及的所有坑,供你排查时对照:

#症状根因解决
1HAL 服务开机不启动.rc 没装到 /vendor/etc/init/init_rc 写在 Android.bpcc_binary
2getService()NoSuchElementExceptionVINTF manifest 没配 / SELinux 拒绝 find检查 .xml + hwservice_contexts + setenforce 0 排查
3hidl-gen 报包名错误package 名和路径不匹配确认 -r vendor.custom:vendor/custom/hardware/interfaces/
4current.txt 哈希不匹配接口改了但没更新哈希hidl-gen -L hash ... >> current.txt
5App 退出 HAL 崩溃悬空 callback + 未捕获异常linkToDeath + null check(第 3 章)
6第二个客户端覆盖第一个单 callback 字段改用 map<pid_t>(第 4 章)
7avc: denied SELinux 拒绝域策略缺权限logcat 看 denied 日志,补 allow 规则
8make update-api 报错新增 @hide 接口没更新 APImake update-api
9defualt 目录名拼错笔误应为 default
10static_libs vs shared_libs 选错纯绑定模式用 staticimpl 用 cc_library_static + service 用 static_libs 链入

9.3 版本演进说明

本文基于 Android 12。后续版本的注意事项:

版本HIDL 状态变化
Android 12✅ 正常使用本文基准
Android 13⚠️ 冻结HIDL 不再接受新接口,新 HAL 走 AIDL;已有 HIDL 仍可维护
Android 14⚠️ 冻结同上,进一步推动 AIDL 迁移

📌 如果你在 Android 13+ 上新建 HAL,请直接用 AIDL(aidl_interface),不要再用 HIDL。本文的知识(.hal 语法、hidl-gen、hwservicemanager、linkToDeath)大部分概念在 AIDL 里都有对应,迁移成本不高。

9.4 后续规划

本文是"自定义 HAL 开发篇"系列的 HIDL 篇。后续计划:

篇章内容状态
HIDL 篇(本文)Android 12 HIDL HAL 完整实战✅ 已完成
AIDL 篇Android 13/14 AIDL HAL 开发📝 规划中
CarWatchdog 篇AAOS 高可用心跳机制详解📝 规划中
VTS 测试篇HAL 的 VTS 兼容性测试📝 规划中
版本升级篇HIDL 1.0 → 1.1 接口升级实战📝 规划中

本文会持续更新到 Android 13/14 的差异说明。如果你在实践过程中遇到问题,欢迎留言交流。

image.png