自定义 Android 系统服务与 HAL 交互全流程指南

一、整体架构:两层服务,两套查找机制

你的服务分为两个层次,分别用不同的"服务管理器"管理:

  1. 系统服务层TestService

    • 运行在 system_server 进程中

    • 通过 ServiceManagerservicemanager)发布,名为 "testtld"

    • 上层 App 通过 ServiceManager.getService("testtld") 获取

    • 对应接口:ITestHalManager

  2. HAL 层(C++ 编译的独立进程)

    • 运行在独立进程 android.hardware.testtld-service

    • 通过 hwservicemanager(或直接注册到 servicemanager,当前暂时如此)发布

    • 描述符为 "android.hardware.testtld.IHelloTest",实例名为 "default"

    • 系统服务通过 IHelloTest.DESCRIPTOR + "/default" 找到它

    • 对应接口:IHelloTest(由 AIDL 生成)

交互链路:

应用层 → ServiceManager.getService("testtld")ITestHalManagerTestServiceIHelloTest.Stub.asInterface() → HAL 服务

二、代码组件一览

1. AIDL 接口定义

HAL 接口: hardware/interfaces/testtld/aidl/android/hardware/testtld/IHelloTest.aidl

java 复制代码
package android.hardware.testtld;

@VintfStability
interface IHelloTest {
    int getTestOne(in int event, in String name);
    void test_write(String str);
    String test_read();
}

由此生成 Java 绑定 android.hardware.testtld-V1-java

系统内部接口: frameworks/base/core/java/android/hardware/testtld/ITestHalManager.aidl

java 复制代码
package android.hardware.testtld;

/**
 * {@hide}
 */
interface ITestHalManager {
    void testhal_write(String str);
    String testhal_read();
}

由此生成 ITestHalManager.Stub,供 TestService 发布。

2. Java 系统服务 TestService.java

路径:frameworks/base/services/core/java/com/android/server/testtld/TestService.java

核心代码:

java 复制代码
import android.hardware.testtld.IHelloTest;
import android.hardware.testtld.ITestHalManager;

public class TestService extends SystemService {
    private IHelloTest mVintfTestHal = null;
    private ITestHalManager mTestHalManager = null;

    private final class TestHalManagerServiceImpl extends ITestHalManager.Stub {
        @Override
        public void testhal_write(String str) throws RemoteException {
            if (mVintfTestHal != null) {
                mVintfTestHal.test_write(str);   // 调用 HAL
            }
        }
        @Override
        public String testhal_read() throws RemoteException {
            if (mVintfTestHal != null) {
                return mVintfTestHal.test_read();
            }
            return "";
        }
    }

    public TestService(Context context) {
        super(context);
        IBinder binder = Binder.allowBlocking(
                ServiceManager.waitForDeclaredService(IHelloTest.DESCRIPTOR + "/default"));
        if (binder != null) {
            mVintfTestHal = IHelloTest.Stub.asInterface(binder);
        }
        mTestHalManager = new TestHalManagerServiceImpl();
    }

    @Override
    public void onStart() {
        publishBinderService("testtld", (IBinder) mTestHalManager);
    }
}

关键字符串映射:
IHelloTest.DESCRIPTOR = "android.hardware.testtld.IHelloTest"

HAL 服务全名 = "android.hardware.testtld.IHelloTest/default"

系统服务发布名 = "testtld"


3. C++ HAL 服务实现

路径:hardware/interfaces/testtld/service.cpp(示例)

java 复制代码
using namespace aidl::android::hardware::testtld;

ndk::ScopedAStatus HelloTest::test_write(const std::string& str) {
    ALOGD("test_write: %s", str.c_str());
    return ndk::ScopedAStatus::ok();
}

ndk::ScopedAStatus HelloTest::test_read(std::string* _aidl_return) {
    *_aidl_return = "hello from HAL";
    return ndk::ScopedAStatus::ok();
}

// 注册服务
int main() {
    ...
    std::shared_ptr<HelloTest> service = ndk::SharedRefBase::make<HelloTest>();
    binder_status_t status = AServiceManager_addService(service->asBinder().get(), "android.hardware.testtld.IHelloTest/default");
    ...
}

三、AIDL 接口冻结与版本控制(重点概念)

为什么需要冻结?

  • 保证硬件接口的兼容性,防止签名被意外修改。

  • 冻结后,系统会生成哈希记录,如果 AIDL 改动,编译时会报错强制升版本。

冻结的时机

修改接口之后 ,确认内容不再变化时执行 freeze-api

正确顺序:先改 .aidl → 再冻结 → 最后编译绑定

兼容 vs 不兼容修改

改动类型 是否需要升级版本 举例
新增方法 不需要(兼容) 增加 test_write,保留 getTestOne
删除方法、改方法名、改参数 必须升版本(例如 V2) 删除 getTestOne 或修改其参数

如何冻结(兼容性修改)

bash 复制代码
m android.hardware.testtld-freeze-api    # 更新 V1 冻结记录
m android.hardware.testtld-V1-java       # 重新编译 java 绑定

Android.bpversions_with_info 无需改动,保持 "1"

如何升级版本(不兼容修改)

  1. 修改 Android.bp,新增 V2 版本信息(保留 V1)

  2. 修改 .aidl

  3. m android.hardware.testtld-freeze-api

  4. 在依赖处改用 android.hardware.testtld-V2-java

四、SELinux 配置(设备专用目录)

所有规则文件放在 device/<vendor>/<board>/sepolicy/ 下,并在 BoardConfig.mk 中添加:

bash 复制代码
BOARD_SEPOLICY_DIRS += device/<vendor>/<board>/sepolicy

需要创建/修改的文件

1. service.te --- 定义系统服务类型
bash 复制代码
type testtld_service, service_manager_type;
2. service_contexts --- 系统服务名称与安全类型映射
bash 复制代码
testtld              u:object_r:testtld_service:s0

安全上下文格式:用户:角色:类型:级别

  • u = SELinux 用户(固定)

  • object_r = 角色(被动实体,固定)

  • testtld_service = 自定义类型(需提前在 service.te 中定义)

  • s0 = 安全级别(最低敏感度,固定)

3. hwservice_contexts --- HAL 服务映射(当前 HAL 直接注册在 servicemanager 时可省略,规范做法应通过 hwservicemanager
bash 复制代码
android.hardware.testtld.IHelloTest::default u:object_r:hal_testtld_default:s0
4. hal_hellotest.te --- 允许 HAL 进程注册服务
bash 复制代码
allow hal_hellotest default_android_service:service_manager add;
  1. system_server.te --- 允许 system_server 注册和查找
bash 复制代码
allow system_server default_android_service:service_manager find; 
allow system_server testtld_service:service_manager add;
6. priv_app.te --- 允许 Launcher 查找和调用
bash 复制代码
allow priv_app testtld_service:service_manager find; 
allow priv_app testtld_service:binder { call transfer };

注意 :Launcher 运行在 priv_app 域(因为装在 /system/priv-app),不是 system_app。规则中的源域必须精确匹配进程的实际域。

调试查看拒绝

复制代码
dmesg | grep avc

五、SELinux 域(Domain)盲区解析

  • 域 = 进程的安全标签中的"类型"部分,类似"工作制服"。

  • Android 根据应用安装位置自动分配域:

    • /system/priv-app/priv_app(黑色制服,高特权,如 Launcher、Settings)

    • /system/app/system_app(蓝色制服,普通系统应用)

    • 系统服务进程 → system_server(白色制服)

  • 权限规则必须源域完全匹配才生效 。给 system_app 的权限不会作用于 priv_app

  • 如何确认进程域:
    ps -Z | grep <进程名> 或查看 SELinux 拒绝日志中的 scontext=u:r:XXX:s0

六、Launcher 调用示例

Launcher.java 已有的 onCreate 中添加:

java 复制代码
import android.os.IBinder;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.hardware.testtld.ITestHalManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // 原有代码 ...

    IBinder binder = ServiceManager.getService("testtld");
    if (binder != null) {
        ITestHalManager manager = ITestHalManager.Stub.asInterface(binder);
        try {
            manager.testhal_write("nihao");
        } catch (RemoteException e) {
            Log.e("Launcher", "write failed", e);
        }
    }
}

七、调试与排错

查看 SELinux 拒绝日志

在设备终端(adb shellsu 获取 root):

bash 复制代码
logcat -c   # 清空缓冲区
# 触发相关操作
logcat -d | grep "avc: denied"

或过滤特定服务:

bash 复制代码
logcat -d | grep -E "testtld|avc.*denied"

根据拒绝日志补写规则

例如日志:

bash 复制代码
avc: denied { find } for pid=1134 name=testtld scontext=u:r:priv_app:s0 tcontext=u:object_r:testtld_service:s0 tclass=service_manager permissive=1

需要添加:

bash 复制代码
allow priv_app testtld_service:service_manager find;

切换 SELinux 模式

  • 查看当前模式:getenforce

  • 设为宽容模式(不阻断):setenforce 0

  • 设为强制模式(阻断并记录):setenforce 1

八、快速检查清单

  • AIDL 文件定义清晰,方法与 Java 调用一致

  • HAL aidl_interfaceAndroid.bp 中 Java 后端已启用 enabled: true

  • services.coreAndroid.bp 中已依赖 android.hardware.testtld-V1-java

  • 系统服务 publishBinderService 的名称与 service_contexts 中的名称完全一致

  • HAL 服务描述符和实例名与 Java 代码中 waitForDeclaredService 的参数完全一致

  • 设备 sepolicy 目录已正确配置并包含在 BoardConfig.mk

  • 所有 avc: denied 日志已通过添加明确规则解决

  • Launcher 所用域(priv_app)的规则已单独添加

相关推荐
JMchen1236 小时前
NDK新趋势——Rust与Android深度集成实战
android·开发语言·rust·jni·内存安全·android ndk·移动端性能
凡情6 小时前
android隐私合规检测
android·unity
私人珍藏库6 小时前
[Android] 自动连点器max1.0
android·app·工具·软件·多功能
zhangphil6 小时前
Android Page3与Flow分页查媒体数据库展示宫格图片列表,Kotlin
android·kotlin
xxjj998a6 小时前
Laravel4.x:PHP开发新纪元
android·数据库
Mr -老鬼6 小时前
EasyClick 安卓CLI全栈专家能力手册
android·自动化·ai编程·easyclick·易点云测
峥嵘life7 小时前
Android 不同的蓝牙音箱连接后声音突变问题分析解决
android·学习
JJay.7 小时前
Android BLE 里,MTU、分包和长数据发送到底该怎么处理
android
2501_915909067 小时前
iOS应用签名的三种方法全解析:从官方到第三方工具
android·ios·小程序·https·uni-app·iphone·webview