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 调用完整示例
本章目标:从 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 (系统服务) :位于
/system的CameraService接收请求,进行权限校验(验证 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 系统服务的标准做法。
8.1 为什么要 Framework 层适配
直接用 IHello.getService() 也能调 HAL,但有几个问题:
- App 权限:普通 App 没有
findhwservice 的 SELinux 权限 - 生命周期:HAL 连接失败时谁来做重连?App 侧写太分散
- 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.aidl | frameworks/base/core/java/android/hello/ | Framework 层回调接口 |
IHelloService.aidl | 同上 | Framework 层服务接口 |
HelloManager.java | 同上 | 对 App 暴露的 Manager |
HelloService.java | frameworks/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 ↔ HIDLHwCallbackWrapper做 callback 适配HelloManager对 App 暴露规范 APIContext.java/SystemServer.java注册服务- App 侧从
getSystemService()获取 Manager
📝 下一章:第 9 章 收尾:总结、踩坑清单与后续
第 9 章 收尾:总结、踩坑清单与后续
9.1 全流程总结
经过九章的迭代,我们从零搭建了一个完整的自定义 HIDL HAL。回顾整个端到端调用链:
┌─────────────────────────────────────────────────────────────────┐
│ 端到端调用链 │
│ │
│ ┌───────┐ 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 |
| ② 踩坑版 | B | App 退出 HAL 崩溃 | (故意不解决,暴露问题) |
| ③ 修复版 | C | 悬空 callback 导致 SIGABRT | linkToDeath 两条防线 |
| ④ 进阶版 | D | 多客户端订阅覆盖 | map<pid_t> 按 PID 隔离 |
9.2 踩坑清单
整理本文涉及的所有坑,供你排查时对照:
| # | 症状 | 根因 | 解决 |
|---|---|---|---|
| 1 | HAL 服务开机不启动 | .rc 没装到 /vendor/etc/init/ | init_rc 写在 Android.bp 的 cc_binary 里 |
| 2 | getService() 抛 NoSuchElementException | VINTF manifest 没配 / SELinux 拒绝 find | 检查 .xml + hwservice_contexts + setenforce 0 排查 |
| 3 | hidl-gen 报包名错误 | package 名和路径不匹配 | 确认 -r vendor.custom:vendor/custom/hardware/interfaces/ |
| 4 | current.txt 哈希不匹配 | 接口改了但没更新哈希 | hidl-gen -L hash ... >> current.txt |
| 5 | App 退出 HAL 崩溃 | 悬空 callback + 未捕获异常 | linkToDeath + null check(第 3 章) |
| 6 | 第二个客户端覆盖第一个 | 单 callback 字段 | 改用 map<pid_t>(第 4 章) |
| 7 | avc: denied SELinux 拒绝 | 域策略缺权限 | logcat 看 denied 日志,补 allow 规则 |
| 8 | make update-api 报错 | 新增 @hide 接口没更新 API | make update-api |
| 9 | defualt 目录名拼错 | 笔误 | 应为 default |
| 10 | static_libs vs shared_libs 选错 | 纯绑定模式用 static | impl 用 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 的差异说明。如果你在实践过程中遇到问题,欢迎留言交流。