Android TA/CA 开发与 Binder 各层适配(含通信演示)
适用:Android 12 ~ 16,TEE 侧以 OP-TEE(GlobalPlatform TEE Client API) 为主线,末尾附 Trusty 对照。
说明:本文的 CA/TA/HAL 代码是可直接照抄的骨架(按 OP-TEE 4.x + Android AIDL HAL 写法),
但未在本机编译验证;第 7 节的运行输出标注为"预期示例",用于对照你自己的环境。
文档地图
全文按 六部分 组织,每部分回答一个问题:
| 部分 | 章节 | 回答的问题 |
|---|---|---|
| 一 · 导览 | 0 ~ 2 | 有哪两条通道?术语怎么对?文件都在哪一层? |
| 二 · 实现 | 3 ~ 5 | 每层要写什么代码、构建怎么配? |
| 三 · 运行 | 6 ~ 7 | 一次调用怎么走完全程?怎么验证跑通了? |
| 四 · 安全 | 8 | 谁能访问 TEE?域怎么划分?.te 与 neverallow 怎么过? |
| 五 · 适配 | 9 ~ 11 | Android 12→16 改了什么?大块数据怎么办?Trusty 有何不同? |
| 六 · 排障 | 12 ~ 13 | 报错怎么定位?速查表在哪? |
三条阅读路径
| 你的目的 | 直接读 |
|---|---|
| 想知道"每个文件放在哪个分区、谁依赖谁" | 2.1 → 2.8(分块图解) |
| 想知道"权限 / SELinux 怎么配、neverallow 怎么过" | 8.1 → 8.9 |
| 想从零跑通一次调用 | 4 → 5 → 7 |
图片索引(源图:android/ta_ca_files_layers.drawio)
| 图 | 文件 | 出现章节 |
|---|---|---|
| 分层结构全景 | img/overview.png | 2.1 |
| 块 A:App + Framework | img/block1.png | 2.2 |
| 块 B:HAL 层(三个子容器) | img/block2.png | 2.3 |
| 块 C:TEE 运行环境 + TA | img/block3.png | 2.4 |
| 块 D:策略与构建 | img/block4.png | 2.5 |
| 图例与结论 | img/block5.png | 2.6 |
第一部分 · 导览:两条通道、术语与全局结构
0. 先分清两条完全不同的"通道"
新手最容易混淆:Android 侧有两个叠在一起的"跨边界调用",机制完全不同。
| 通道 | 跨越的边界 | 机制 | 你是谁 |
|---|---|---|---|
| Android Binder | app ↔ system_server ↔ vendor HAL 进程 | binder 驱动 + Parcel + AIDL 桩 | Binder 客户端/服务端 |
| TEE Session | 普通世界(REE)↔ 安全世界(TEE) | /dev/tee0 ioctl + 共享内存 + SMC/HVC | CA(Client Application)/ TA(Trusted Application) |
关键点:CA 不是 Binder 服务,TA 更不是。Binder 只负责把请求送到"能调 TEEC 的那个进程"(通常是 vendor HAL 或某 native service);从那里开始才是 TEEC → 内核驱动 → TEE。
App(Java/Kotlin)
│ Binder(app 侧代理)
▼
SystemServer / Framework Service(system 分区)
│ Binder(AIDL,stable interface)
▼
Vendor HAL 进程(/vendor/bin/hw/android.hardware.xxx-service)
│ ★ 进程内直接调用 libteec(不是 Binder)
▼
libteec.so → ioctl(/dev/tee0) → optee.ko → SMC/HVC → TEE OS
│
▼
TA(<uuid>.ta,运行在 TEE 内)
1. 概念对齐:TA / CA / Session / Command
| 术语 | 含义 | 关键约束 |
|---|---|---|
| TA(Trusted Application) | 运行在 TEE 内、有独立地址空间与权限的应用 | 用 UUID 标识;只能通过 TEE 暴露的 API(加密、安全存储、时钟)访问外部资源 |
| CA(Client Application) | REE 侧调用 TA 的普通进程 | 需要 libteec + 访问 /dev/tee0 的权限 |
| Session | CA 与 TA 之间的一次会话上下文 | TEEC_OpenSession 建立,TEEC_CloseSession 关闭;TA 侧 TA_OpenSessionEntryPoint |
| Command | 会话内的一次调用 | TEEC_InvokeCommand(session, cmd_id, op) → TA_InvokeCommandEntryPoint |
| Param | 参数传递的 4 个槽位 | VALUE(≤2×32bit)/ TEMP_MEMREF / WHOLE_MEMREF / PARTIAL_MEMREF |
| Shared Memory | 跨世界共享的物理连续/注册内存 | TEEC_AllocateSharedMemory / TEEC_RegisterSharedMemory |
为什么"4 个参数、每个 ≤ 固定大小":TEE 的 SMC 调用寄存器只有有限几个,GP 规范把参数压成"类型 + 4 个值/引用",大块数据必须走共享内存(MEMREF)。
2. 分层结构图:一张图拆成六块看
源文件:
android/ta_ca_files_layers.drawio(VS Code 装 draw.io 扩展可直接打开 / 导出 PNG)。
下面 2.1~2.6 是同一张图的 6 个局部截图,可以单独看,也可以回到 2.1 的全景对位。
2.1 全景:六个块怎么分

这张图从左到右、从上到下是 5 个纵向层容器 + 右侧 2 个配置列:
| 块 | 图中位置 | 内容 | 参与运行时? |
|---|---|---|---|
| A | 左上(第 ①② 层) | App 进程、system_server 里的系统服务 | ✅ binder 段 |
| B | 中左(第 ③ 层,最大) | AIDL 接口包 / HAL 服务 / CA 库 | ✅ binder 终点 + TEE 起点 |
| C | 下方(第 ④⑤ 层) | TEE 驱动与 /dev/tee0、tee-supplicant、TA | ✅ TEE session 段 |
| D | 右侧两列 | SELinux 策略、构建与打包 | ❌ 只决定"能不能跑 / 装到哪" |
看图顺序:2.1 全景 → 顺着箭头走 A→B→C(这就是一次调用的真实路径)→ 出问题时再回来看 D。
2.2 块 A:① App 层 + ② Framework 层

| 文件 | 分区 / 进程 | 作用 | 不写会怎样 |
|---|---|---|---|
AndroidManifest.xml | /system 或 /data | 声明 package、uses-permission(如 USE_HELLO) | 拿不到自定义权限,调用被 SecurityException 拒绝 |
HelloManager.java | 同 App 进程 | App 侧唯一入口:ServiceManager.getService("hello") → IHelloService.Stub.asInterface | App 被迫直接碰 libteec(不可接受) |
privapp-permissions-hello.xml | /system/etc/permissions/ | 特权 app 的权限 allowlist | signature|privileged 权限只给签名不给特权 → 启动即崩 |
IHelloService.aidl | /system(framework 接口) | 生成 IHelloService.Stub / Stub.Proxy | 无跨进程桩 |
HelloService.java | system_server 进程 | extends IHelloService.Stub,@EnforcePermission 校验后再 binder 到 HAL | 任何 app 都能调 TEE(越权) |
| SystemServer 注册 | system_server(SELinux 域 system_server) | ServiceManager.addService("hello", new HelloService(ctx)) | getService 返回 null |
这一块里只有两条线:
- 实线
binder:HelloManager──▶HelloService(跨进程,App 进程 →system_server) - 虚线
实现/注册:IHelloService.aidl──▶HelloService(编译期生成 + 运行时注册)
要点:App 层只声明权限、只调 Framework API;任何一层都不该让 App 直接
dlopen("libteec.so")——那等于把 TEE 的全部攻击面暴露给普通应用。
2.3 块 B:③ HAL 层(三个子容器,最容易搞混的一块)

第 ③ 层内部其实是 三件互相独立的东西,只是都被放在 /vendor 分区:
| 子容器 | 关键文件 | 性质 | 作用 |
|---|---|---|---|
| AIDL 接口包(绿) | aidl/android/hardware/hello/IHello.aidl、aidl_api/versions_with_info、Android.bp 里的 aidl_interface | 纯接口,stability: "vintf" | 生成 BnHello(服务端)/ BpHello(客户端);aidl_api/ 是冻结快照,改了接口就得升版本 |
| HAL 服务(蓝) | service.cpp(BnHello 实现)、Android.bp(cc_binary + init_rc + vintf_fragments)、android.hardware.hello-service.rc、android.hardware.hello.xml(VINTF) | 一个常驻 vendor 进程 | 注册到 servicemanager、被 init 拉起、向 framework 声明"我提供 hello 1.0" |
| CA 库(橙) | hello_ca.c / libhello_tee_client.c、hello_ta.h、Android.bp(shared_libs: ["libteec"], vendor: true)、libteec.so、hello_ca(CLI) | 普通 C 库 | 真正调 TEEC_* 的地方 |
这一块里的三条线:
IHello.aidl ──生成 BnHello──▶ service.cpp (绿虚线,编译期)
service.cpp ──同进程调用───▶ CA 库 (橙实线,★ 不是 binder!)
CA 库 ──open + ioctl───────▶ /dev/tee0 (红实线,出 REE)
最容易踩的三个坑
- 把 CA 当成 binder 服务:CA 只是被 HAL 进程链接进去的一个库(少数方案会单独起 native service 再 binder 封装,但那是外层再包一层)。
IHello.aidl与android.hardware.hello.xml的版本号不一致 → HAL 起不来、service list里找不到。hello_ca(CLI)只是调试/产线自测工具(虚线框),不要让它成为正式通路。
2.4 块 C:④ TEE 运行环境 + ⑤ TA 层

| 文件 / 节点 | 分区 | 作用 | 备注 |
|---|---|---|---|
/dev/tee0、/dev/teepriv0 | devtmpfs | 字符设备节点 | tee0 给普通 CA 与 tee-supplicant;teepriv0 给特权客户端 |
optee.ko | vendor_dlkm 或 vendor_boot | TEE 驱动 | GKI 设备必须放 vendor_dlkm,放错位置 = 驱动不加载 |
tee-supplicant | /vendor/bin/ | 常驻用户态代理:加载 TA、RPMB、文件系统/时间代理 | 它不在时 TEEC_OpenSession 直接失败 |
/vendor/lib/optee_armtz/<uuid>.ta | /vendor | TA 二进制(按 UUID 命名) | 文件名必须与 TA_UUID 一致 |
ta/src/hello_ta.c | TEE OS 内 | 五个入口:Create/Destroy/OpenSession/CloseSession/InvokeCommand | 命令分发都在 InvokeCommand |
user_ta_header_defines.h | TEE OS 内 | TA_UUID / TA_FLAGS / 栈与堆大小 | 与 CA 侧 UUID 必须逐字节一致 |
ta/Makefile + sub.mk | 构建机 | 指定 TA_DEV_KIT_DIR、BINARY=<uuid> | 产物 <uuid>.ta |
这一块里的三条线:
tee-supplicant──加载/校验──▶<uuid>.taoptee.ko──SMC/HVC 进入安全世界──▶hello_ta.cta/Makefile──编译产物──▶<uuid>.ta(虚线,构建期)
要点:从 ③ 到 ⑤ 的物理边界只有一处——
SMC/HVC指令。/dev/tee0之后仍是普通世界的内核态;真正"换世界"发生在optee.ko发 SMC 的那一刻。
2.5 块 D:策略与构建(不参与运行时,但决定成败)

| 列 | 文件 | 决定什么 | 配错的典型现象 |
|---|---|---|---|
| 策略(红) | hal_hello_default.te | HAL 域能否 allow tee_device | TEEC_ERROR_ACCESS_DENIED + avc: denied |
file_contexts | /dev/tee0、TA 目录打什么标签 | ls -Z 显示 u:object_r:device:s0(未打标签) | |
seapp_contexts | App 落哪个域 | app 起不来 / 被 neverallow 拦 | |
mac_permissions.xml | 签名 → seinfo | 特权权限判定异常 | |
system/sepolicy/{private,public,vendor} | 策略的分层归属(Treble) | vendor 策略引用了 private 类型 → 编译报 neverallow | |
neverallow(AOSP 内) | 编译期断言 | 编译直接失败,不会留到运行时 | |
| 构建(绿) | Android.bp | 编出 aidl_interface / cc_binary / cc_library | 模块不存在 |
PRODUCT_PACKAGES | HAL 服务、CLI 进镜像 | 镜像里没有可执行文件 | |
PRODUCT_COPY_FILES | TA → /vendor/lib/optee_armtz | TEEC_ERROR_ITEM_NOT_FOUND | |
BOARD_VENDOR_SEPOLICY_DIRS | 策略目录被编进 vendor policy | .te 写了却不生效 | |
device.mk / product.mk | seinfo、分区归属 | 权限判定 / OTA 异常 |
两条虚线:hal_hello_default.te ──授予 tee_device──▶ HAL 域;PRODUCT_COPY_FILES ──安装──▶ optee_armtz。
2.6 图例与三条结论

| 线型 / 底色 | 含义 |
|---|---|
| 黑 / 蓝实线 | 运行时调用(同进程调用 / binder 跨进程) |
| 红实线 | 出 REE:open + ioctl → SMC/HVC |
| 绿 / 灰虚线 | 编译期生成、构建期安装、策略授权 |
| 黄底框 | CA / TA 共用头文件 hello_ta.h |
| 红底容器 | 安全设备与策略 |
三条结论(记住这三句,这张图就不用背了)
- 运行链只有两条:
- 唯一的"共用点"是
hello_ta.h:TA_UUID与命令号 CA/TA 必须完全一致——这是联调第一大坑(现象:TEEC_ERROR_ITEM_NOT_FOUND或BAD_PARAMETERS)。 - 策略与构建不参与运行时,但决定"能不能跑、装到哪":配错时表现为
TEEC_ERROR_ACCESS_DENIED(策略)或TEEC_ERROR_ITEM_NOT_FOUND(没装上 / UUID 不符)。
2.7 反过来看:每层要改什么(适配清单)
| 层 | 适配内容 | 典型配置/文件 |
|---|---|---|
| App | 只调 Framework API;不要直接 dlopen libteec | SystemService + 权限声明 |
| Framework Service | 包装成系统服务,做权限/参数校验 | frameworks/base/services/...、@EnforcePermission |
| AIDL HAL | 稳定接口(stability: "vintf")、版本冻结、vendor: true | aidl_interface { }、versions_with_info |
| HAL 实现 | 注册到 servicemanager、由 init 拉起、VINTF fragment 声明 | cc_binary、.rc、manifest.xml |
| CA(native) | 链接 libteec、处理 TEEC_* 返回码与 err_origin | shared_libs: ["libteec"] |
| 内核 | TEE 驱动(optee.ko)+ /dev/tee0、/dev/teepriv0 节点 | GKI 场景下放 vendor_dlkm 或 vendor_boot |
| TEEC 运行环境 | tee-supplicant 常驻(TA 加载、RPMB、文件系统代理) | init.rc 里的 service |
| TA | 编译成 <uuid>.ta,装到 /vendor/lib/optee_armtz/ | TA_DEV_KIT_DIR + BINARY=<uuid> |
| SELinux | HAL 域访问 tee_device、tee_supplicant socket、tee_data_file | hal_xxx_default.te |
| VINTF | HAL 的 manifest.xml 与 framework 的 compatibility_matrix.xml 对齐 | vintf_fragments |
| 构建 | vendor 分区、vendor: true、32/64 位一致 | Android.bp / PRODUCT_PACKAGES |
2.8 五层文件总表与依赖关系清单
下面是把 2.2~2.5 四张分块图合并成的一张速查表,按"层 → 目录 → 文件 → 作用"排列。
| 层 | 目录/分区 | 关键文件 | 作用 |
|---|---|---|---|
| ① App | /system 或 /data | AndroidManifest.xml、HelloManager.java、privapp-permissions-hello.xml | 声明权限、调 Framework API;特权 app 需 allowlist |
| ② Framework | /system(system_server) | IHelloService.aidl、HelloService.java、SystemServer 注册 | 包一层系统服务,做权限校验,再 binder 到 HAL |
| ③ HAL(AIDL 接口) | /vendor | aidl/android/hardware/hello/IHello.aidl、aidl_api/(冻结快照)、Android.bp 里的 aidl_interface | 稳定接口定义,生成 BnHello/BpHello |
| ③ HAL(服务实现) | /vendor/bin/hw/ | service.cpp、Android.bp(cc_binary + init_rc + vintf_fragments)、*.rc、*.xml(VINTF) | 注册 Binder 服务、被 init 拉起、声明 HAL 版本 |
| ③ CA(客户端库) | /vendor/lib*/ | hello_ca.c / libhello_tee_client.c、hello_ta.h(CA/TA 共用)、Android.bp(shared_libs: ["libteec"]) | 调 TEEC_*;hello_ca CLI 仅用于调试/产线自测 |
| ④ TEE 运行环境 | vendor / vendor_dlkm / vendor_boot | optee.ko、tee-supplicant、/dev/tee0、/dev/teepriv0、/vendor/lib/optee_armtz/<uuid>.ta | 驱动 + 用户态代理 + 设备节点 + TA 存放目录 |
| ⑤ TA | TEE OS 内 | ta/src/hello_ta.c、user_ta_header_defines.h(UUID/flags/栈大小)、ta/Makefile + sub.mk | 五个入口函数 + 命令分发;编译产物 <uuid>.ta |
| 策略 | system/sepolicy、device/.../sepolicy | hal_hello_default.te、file_contexts、seapp_contexts、mac_permissions.xml、neverallow(AOSP 内) | 决定"谁能访问 TEE、文件打什么标签、进程落哪个域" |
| 构建 | 设备配置 | PRODUCT_PACKAGES、PRODUCT_COPY_FILES(TA 装到 /vendor/lib/optee_armtz)、BOARD_VENDOR_SEPOLICY_DIRS、device.mk | 决定"编什么、装到哪、策略从哪来" |
关键依赖关系(与图中连线一一对应):
AndroidManifest ──▶ HelloManager ──binder──▶ HelloService(system_server)
IHelloService.aidl ──实现──▶ HelloService
HelloService ──binder + IHello──▶ service.cpp
IHello.aidl ──生成 BnHello──▶ service.cpp
service.cpp ──同进程调用──▶ CA 库 ──▶ libteec.so ──open+ioctl──▶ /dev/tee0
/dev/tee0 ──▶ optee.ko ──SMC/HVC──▶ TA(hello_ta.c)
tee-supplicant ──加载/校验──▶ /vendor/lib/optee_armtz/<uuid>.ta
hello_ta.h ──共用 UUID/命令号──▶ CA 与 TA(两侧必须完全一致)
ta/Makefile ──编译产物──▶ <uuid>.ta ──PRODUCT_COPY_FILES──▶ /vendor/lib/optee_armtz
hal_hello_default.te ──授予 tee_device──▶ HAL 域
file_contexts ──打标签──▶ /dev/tee0、TA 目录
图与表对不上时以图为准(图由
ta_ca_files_layers.drawio直接导出);三条结论见 2.6。
第二部分 · 实现:TA / CA / HAL / Service / App 五步落地
3. GP TEE Client API 速查(CA 侧必背)
| 函数 | 作用 | 常见返回值 |
|---|---|---|
TEEC_InitializeContext(name, ctx) | 打开 TEE 上下文(本质 open /dev/tee0) | TEEC_SUCCESS / TEEC_ERROR_ITEM_NOT_FOUND |
TEEC_OpenSession(ctx, sess, uuid, login, op, res, &origin) | 与 TA 建立会话 | TEEC_ERROR_ITEM_NOT_FOUND(TA 没装/未加载)、TEEC_ERROR_ACCESS_DENIED |
TEEC_InvokeCommand(sess, cmd, op, &origin) | 发命令 | TEEC_ERROR_BAD_PARAMETERS、TEEC_ERROR_NOT_IMPLEMENTED |
TEEC_CloseSession / TEEC_FinalizeContext | 释放 | — |
TEEC_AllocateSharedMemory / TEEC_RegisterSharedMemory / TEEC_ReleaseSharedMemory | 大块数据传递 | — |
TEEC_RequestCancellation(op) | 取消阻塞中的调用 | — |
err_origin 很重要:区分是"REE 侧(TEE 客户端库)出错"还是"TA 返回的错"——排障时先看它。
| 错误码 | 值 | 含义 |
|---|---|---|
TEEC_SUCCESS | 0x00000000 | 成功 |
TEEC_ERROR_ACCESS_DENIED | 0xFFFF0001 | 权限/SELinux/会话限制 |
TEEC_ERROR_BAD_PARAMETERS | 0xFFFF0006 | 参数类型或个数不对 |
TEEC_ERROR_ITEM_NOT_FOUND | 0xFFFF0008 | TA 不存在 / UUID 不匹配 / 装错路径 |
TEEC_ERROR_NOT_IMPLEMENTED | 0xFFFF000C | 命令号未实现 |
TEEC_ERROR_COMMUNICATION | 0xFFFF000E | 与 TEE 通信失败(驱动/固件问题) |
TEEC_ERROR_SHORT_BUFFER | 0xFFFF0010 | 输出缓冲不足 |
4. 从零跑通:目录与代码骨架
4.1 目录结构(建议)
hello-tee/
├── ta/
│ ├── Makefile
│ ├── sub.mk
│ ├── include/hello_ta.h # UUID + 命令号(CA/TA 共用)
│ └── src/hello_ta.c
├── ca/
│ ├── src/hello_ca.c
│ └── Android.bp # vendor: true, shared_libs: ["libteec"]
├── hal/
│ ├── aidl/android/hardware/hello/IHello.aidl
│ ├── aidl_api/ # 冻结版本快照
│ ├── service.cpp
│ ├── Android.bp
│ ├── android.hardware.hello-service.rc
│ └── android.hardware.hello.xml # VINTF fragment
└── sepolicy/
└── hal_hello_default.te
4.2 共用头文件 ta/include/hello_ta.h
#define TA_HELLO_UUID \
{ 0x6b8f1c2a, 0x4d3e, 0x4a5b, \
{ 0x9c, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77 } }
#define TA_HELLO_CMD_ADD 1 /* in: value a,b ; out: value a = a+b */
#define TA_HELLO_CMD_ECHO 2 /* in/out: temp memref */
4.3 TA 实现 ta/src/hello_ta.c
#include <tee_internal_api.h>
#include <tee_internal_api_extensions.h>
#include "hello_ta.h"
TEE_Result TA_CreateEntryPoint(void) { return TEE_SUCCESS; }
void TA_DestroyEntryPoint(void) { }
TEE_Result TA_OpenSessionEntryPoint(uint32_t pt, TEE_Param p[4], void **sess_ctx)
{
(void)pt; (void)p;
*sess_ctx = NULL;
return TEE_SUCCESS;
}
void TA_CloseSessionEntryPoint(void *sess_ctx) { (void)sess_ctx; }
TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx, uint32_t cmd,
uint32_t pt, TEE_Param p[4])
{
(void)sess_ctx;
switch (cmd) {
case TA_HELLO_CMD_ADD:
if (pt != TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_INPUT,
TEE_PARAM_TYPE_VALUE_OUTPUT,
TEE_PARAM_TYPE_NONE))
return TEE_ERROR_BAD_PARAMETERS;
p[2].value.a = p[0].value.a + p[1].value.a;
return TEE_SUCCESS;
case TA_HELLO_CMD_ECHO:
if (pt != TEE_PARAM_TYPES(TEE_PARAM_TYPE_MEMREF_INOUT,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE,
TEE_PARAM_TYPE_NONE))
return TEE_ERROR_BAD_PARAMETERS;
/* 示例:把首字节改写,验证共享内存双向可见 */
if (p[0].memref.size < 1)
return TEE_ERROR_SHORT_BUFFER;
((uint8_t *)p[0].memref.buffer)[0] ^= 0xFF;
return TEE_SUCCESS;
default:
return TEE_ERROR_NOT_IMPLEMENTED;
}
}
4.4 TA 构建 ta/Makefile
# 交叉编译:需先准备 optee_os 的 ta-dev-kit
CROSS_COMPILE ?= aarch64-linux-gnu-
TA_DEV_KIT_DIR ?= $(OPTEE_OS)/out/arm-plat-*/export-ta_arm64
BINARY := $(shell python3 -c "import uuid;print(uuid.UUID('6b8f1c2a-4d3e-4a5b-9c11-223344556677'))")
CFG_TEE_TA_LOG_LEVEL ?= 4
include $(TA_DEV_KIT_DIR)/mk/ta_dev_kit.mk
产物:<uuid>.ta(已签名)→ 推到设备 /vendor/lib/optee_armtz/。
4.5 CA 实现 ca/src/hello_ca.c
#include <stdio.h>
#include <string.h>
#include <tee_client_api.h>
#include "hello_ta.h"
static const TEEC_UUID kHelloUuid = TA_HELLO_UUID;
int main(void)
{
TEEC_Context ctx;
TEEC_Session sess;
TEEC_Operation op;
uint32_t origin = 0;
TEEC_Result res;
char buf[32] = "hello-tee";
res = TEEC_InitializeContext(NULL, &ctx);
if (res != TEEC_SUCCESS) { printf("init ctx fail 0x%x\n", res); return 1; }
printf("TEEC_InitializeContext: OK\n");
res = TEEC_OpenSession(&ctx, &sess, &kHelloUuid,
TEEC_LOGIN_PUBLIC, NULL, NULL, &origin);
if (res != TEEC_SUCCESS) {
printf("open session fail 0x%x origin %u\n", res, origin); return 1;
}
printf("TEEC_OpenSession: OK\n");
/* 命令 1:3 + 5 */
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INPUT, TEEC_VALUE_INPUT,
TEEC_VALUE_OUTPUT, TEEC_NONE);
op.params[0].value.a = 3;
op.params[1].value.a = 5;
res = TEEC_InvokeCommand(&sess, TA_HELLO_CMD_ADD, &op, &origin);
if (res != TEEC_SUCCESS) { printf("invoke add fail 0x%x\n", res); return 1; }
printf("CMD_ADD: 3 + 5 = %u\n", op.params[2].value.a);
/* 命令 2:共享内存回环 */
memset(&op, 0, sizeof(op));
op.paramTypes = TEEC_PARAM_TYPES(TEEC_MEMREF_TEMP_INOUT, TEEC_NONE,
TEEC_NONE, TEEC_NONE);
op.params[0].tmpref.buffer = buf;
op.params[0].tmpref.size = sizeof(buf);
res = TEEC_InvokeCommand(&sess, TA_HELLO_CMD_ECHO, &op, &origin);
if (res != TEEC_SUCCESS) { printf("invoke echo fail 0x%x\n", res); return 1; }
printf("CMD_ECHO: buf[0] = 0x%02x (原 'h'=0x68 取反)\n", (unsigned char)buf[0]);
TEEC_CloseSession(&sess);
TEEC_FinalizeContext(&ctx);
printf("done\n");
return 0;
}
4.6 CA 的 Android.bp(vendor 侧)
cc_binary {
name: "hello_ca",
srcs: ["ca/src/hello_ca.c"],
vendor: true, // 必须:CA 跑在 vendor 侧
shared_libs: ["libteec"],
cflags: ["-Wall", "-Werror"],
}
libteec若来自 AOSP 且未标vendor_available,需要在你的树里加一个 vendor 版本
(cc_library_shared { name: "libteec", vendor: true, ... }),不要指望从/system链接到 vendor 进程。
5. 把 TEE 能力暴露给 App:AIDL HAL + Service
5.1 AIDL 接口 hal/aidl/android/hardware/hello/IHello.aidl
package android.hardware.hello;
@VintfStability
interface IHello {
int add(int a, int b); // 返回和;错误抛异常或返回负数
byte[] echo(in byte[] in); // 演示大/小块数据
}
5.2 AIDL 定义 hal/Android.bp
aidl_interface {
name: "android.hardware.hello",
srcs: ["aidl/android/hardware/hello/IHello.aidl"],
stability: "vintf", // 稳定:可跨 system/vendor 边界
vendor: true,
versions_with_info: [{ version: "1", imports: [] }],
}
5.3 HAL 实现(调用 CA 的同一套 TEEC 代码)
// hal/service.cpp(节选)
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <aidl/android/hardware/hello/BnHello.h>
using aidl::android::hardware::hello::BnHello;
class Hello : public BnHello {
public:
ndk::ScopedAStatus add(int32_t a, int32_t b, int32_t* out) override {
// 这里调用 4.5 中的 TEEC 逻辑(可抽成 hello_tee_invoke_add())
int32_t sum = 0;
int rc = hello_tee_invoke_add(a, b, &sum); // 内部 TEEC_*
if (rc != 0) return ndk::ScopedAStatus::fromServiceSpecificError(rc);
*out = sum;
return ndk::ScopedAStatus::ok();
}
};
int main() {
ABinderProcess_setThreadPoolMaxThreadCount(0);
std::shared_ptr<Hello> hello = ndk::SharedRefBase::make<Hello>();
const std::string name = std::string() + Hello::descriptor + "/default";
binder_status_t st = AServiceManager_addService(hello->asBinder().get(), name.c_str());
if (st != STATUS_OK) return 1;
ABinderProcess_joinThreadPool();
return 0;
}
5.4 init 与 VINTF
# hal/android.hardware.hello-service.rc
service vendor.hello-default /vendor/bin/hw/android.hardware.hello-service
class hal
user system
group system
seclabel u:r:hal_hello_default:s0
<!-- hal/android.hardware.hello.xml(VINTF fragment) -->
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.hello</name>
<version>1</version>
<fqname>IHello/default</fqname>
</hal>
</manifest>
cc_binary {
name: "android.hardware.hello-service",
vendor: true,
srcs: ["hal/service.cpp"],
shared_libs: ["libbinder_ndk", "libteec", "android.hardware.hello-V1-ndk"],
init_rc: ["hal/android.hardware.hello-service.rc"],
vintf_fragments: ["hal/android.hardware.hello.xml"],
}
5.5 Framework / App
- Framework:写一个
HelloManager(SystemService),通过IServiceManager::waitForDeclaredService("android.hardware.hello.IHello/default")拿代理,加@EnforcePermission。 - App:只调
HelloManager.add(),不要在 App 里dlopen("libteec.so")—— 一是 SELinux 不允许,二是 App 进程拿不到/dev/tee0。
第三部分 · 运行:一次调用的完整时序与验证
6. 一次完整调用的时序
注意:共享内存(MEMREF)的数据不走 binder——数据在
/dev/tee0的 ioctl 里由内核映射;
但 HAL → App 这一段的byte[]仍然走 binder,受 1MB 事务上限约束(见第 10 节)。
7. 演示与验证
7.0 没有板子时:先在 QEMU 上把"TEE 通信"跑通
这一层验证的是最底下的 CA ↔ TA 通路(与 Android 无关),用来排除"代码写错"还是"平台没配好":
# 1) 取 OP-TEE 官方工程(qemu_v8)
mkdir optee && cd optee
repo init -u https://github.com/OP-TEE/manifest.git -m qemu_v8.xml
repo sync -j8
# 2) 编译工具链 + 全栈(optee_os / optee_client / ATF / U-Boot / Linux)
make -j"$(nproc)" toolchains
make -j"$(nproc)"
# 3) 启动 QEMU:normal world 进 Linux,tee-supplicant 由 init 拉起
make run
进入 normal world 的 Linux 后:
ls -l /dev/tee* # /dev/tee0 /dev/teepriv0
optee_example_hello_world # 官方示例:TA 自增计数器
xtest 2>/dev/null | tail -3 # 全套 TEE 用例(可选,验证平台完整性)
预期输出(示例,具体文案随 optee_examples 版本略有差异)
Invoking TA to increment 0
TA incremented value to 1
Invoking TA to increment 1
TA incremented value to 2
这一步通过 = TA 加载、会话、命令、参数回传、shared memory 全部正常;
之后再把同样的 TEEC 代码搬进 Android HAL,剩下要解决的只有"权限 + 打包 + SELinux"。
7.1 最快验证:直接在设备上跑 CA(不经过 binder)
# 1) 推 TA 与 CA(需 root 或 debuggable)
adb push <uuid>.ta /vendor/lib/optee_armtz/
adb push hello_ca /data/local/tmp/
adb shell chmod 755 /data/local/tmp/hello_ca
# 2) 确认 TEE 设备与 supplicant 在跑
adb shell ls -l /dev/tee0 /dev/teepriv0
adb shell getprop | grep -i tee
adb shell ps -A | grep tee_supplicant
# 3) 跑(先确认 SELinux 允许,见第 8 节)
adb shell /data/local/tmp/hello_ca
预期输出(示例)
TEEC_InitializeContext: OK
TEEC_OpenSession: OK
CMD_ADD: 3 + 5 = 8
CMD_ECHO: buf[0] = 0x97 (原 'h'=0x68 取反)
done
7.2 走完整 binder 链路
adb shell service list | grep hello # 应看到 android.hardware.hello.IHello/default
adb shell cmd hello add 3 5 # 若你实现了 cmd 子命令
adb logcat -s HelloHal HelloService # HAL/Framework 侧日志
adb shell dmesg | grep -i optee # 驱动层:会话/命令、SMC 次数
预期输出(示例)
# service list
... android.hardware.hello.IHello/default ...
# cmd hello add 3 5
8
# dmesg
[ 12.345678] optee: initialized driver
[ 20.112233] optee: session 00000000-... opened
[ 20.113001] optee: invoke cmd 1
7.3 自检清单
| 检查项 | 命令/位置 | 期望 |
|---|---|---|
| TEE 驱动加载 | lsmod | grep optee、dmesg | grep optee | 有 initialized |
| 设备节点 | ls -lZ /dev/tee0 | 存在,SELinux 标签为 tee_device |
| TA 是否装对 | ls /vendor/lib/optee_armtz/ | 有 <uuid>.ta,UUID 与 CA 一致 |
| supplicant | ps -A | grep tee_supplicant | 常驻 |
| 权限 | dmesg/logcat 里的 avc: denied | 无(或按第 8 节补策略) |
| HAL 注册 | service list、dumpsys | 有 IHello/default |
第四部分 · 安全:权限模型、SELinux 域与 neverallow
8. 权限模型与域划分(Android 权限 / SELinux 域 / TEE 登录)
8.1 三层权限,各管一段,缺一不可
| 层 | 管什么 | 谁来配 | 失败表现 |
|---|---|---|---|
| Android 权限 | app 有没有资格"发起这个请求" | AndroidManifest + @EnforcePermission | SecurityException |
| SELinux 域 | 进程能不能 open /dev/tee0、连 supplicant、注册 binder 服务 | sepolicy/*.te、file_contexts | avc: denied → TEEC_ERROR_ACCESS_DENIED |
| TEE login 身份 | TA 认不认这个调用方(uid/gid/application id) | TEEC_OpenSession(login, ...) + TA 侧校验 | TA 返回 TEE_ERROR_ACCESS_DENIED |
记忆法:权限决定"能不能提请求",域决定"能不能碰设备",login 决定"TA 认不认你"。
8.2 Android 侧的应用域划分(UID + SELinux domain)
| 应用/进程形态 | UID 来源 | 典型 SELinux 域 | 能否访问 TEE 设备 |
|---|---|---|---|
| 三方应用(Play 安装) | u0_aXX | untrusted_app(按 targetSdk 细分 untrusted_app_27/29/30/31…) | ❌ 默认禁止 |
| 预装、非特权 | system/u0_aXX | system_app(platform 签名) | ❌ 默认禁止 |
预装特权(priv-app) | system | priv_app | ❌ 默认禁止(除非显式加规则,且不推荐) |
| platform 签名普通应用 | u0_aXX | platform_app | ❌ |
| system_server | system | system_server | ❌(应通过 HAL) |
隔离进程(isolatedProcess) | 无名 UID | isolated_app | ❌ |
| Instant App | 动态 UID | ephemeral_app | ❌ |
| 厂商 HAL | system/vendor | hal_xxx_default | ✅(唯一常见被授权方) |
tee-supplicant | system/root | 独立域(tee_supplicant 或厂商自定义) | ✅(它是 TEE 的代理,不是 TA 客户端) |
su / root 调试 | root | su | 调试可用,不可作为产品方案 |
域是怎么被选中的:不是由包名决定,而是 seapp_contexts 按条件匹配(seinfo、isPrivApp、isSystemServer、minTargetSdkVersion…):
# system/sepolicy/private/seapp_contexts(示意,实际条目随版本不同)
user=_app isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=user
user=system seinfo=platform domain=system_app type=system_data_file levelFrom=user
user=_app isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=user
user=_app minTargetSdkVersion=29 domain=untrusted_app_29 type=app_data_file levelFrom=user
user=_app domain=untrusted_app type=app_data_file levelFrom=user
结论:"换个包名/换个签名"不会自动获得 TEE 权限;要新域就必须改
seapp_contexts+ 写.te+ 过 neverallow。
8.3 Android 权限类型(决定"谁可以请求")
| 类型 | 声明方式 | 谁可以拿到 | 用途 |
|---|---|---|---|
normal | <permission android:protectionLevel="normal"> | 任意 app,安装即得 | 低风险能力 |
dangerous | 同上 dangerous | 需运行时申请 + 用户授权 | 隐私类(相机、位置…) |
signature | signature | 与声明者同签名的 app | 平台内部能力 |
signature|privileged | 组合 | 平台签名 或 预装特权 app(+ allowlist) | 系统级但非核心 |
internal | 不导出 | 仅系统内部 | 不建议用于厂商能力 |
特权应用要显式 allowlist(否则开机报 privapp-permissions 违规):
<!-- vendor/etc/permissions/privapp-permissions-vendor-hello.xml(路径随分区/版本,需核对) -->
<permissions>
<privapp-permissions package="com.vendor.hello">
<permission name="android.permission.HELLO_TEE_ACCESS"/>
</privapp-permissions>
</permissions>
Framework 侧做二次校验:
@RequiresPermission("android.permission.HELLO_TEE_ACCESS")
public int add(int a, int b) { ... }
8.4 核心规则:谁能访问 TEE 设备
| 规则 | 说明 |
|---|---|
| 只给 HAL 域 | allow hal_xxx_default tee_device:chr_file rw_file_perms;,不要给 appdomain/system_app |
| appdomain 默认被 neverallow 挡住 | AOSP 用 neverallow 禁止通用应用域直接访问 TEE 设备(条目随版本变化,可在 system/sepolicy 中搜 tee_device 核对) |
| system_server 不直连 | system_server 通过 binder 调 HAL,避免把 TEE 权限扩散到系统进程 |
| 一域一能力 | 多个 HAL 需要 TEE 时,共用同一域或各自加规则,不要复用 system_app 域 |
| 调试用 root 不算方案 | su 下能跑通 ≠ 产品可用;必须按正式域验证 |
8.5 SELinux 规则模板(HAL 扮演 CA)
# sepolicy/hal_hello_default.te(示意,宏名/类型名需按你树的定义调整)
type hal_hello_default, domain;
type hal_hello_service, service_manager_type;
hal_server_domain(hal_hello_default, hal_hello)
hal_client_domain(system_server, hal_hello)
# 1) 访问 TEE 字符设备
allow hal_hello_default tee_device:chr_file rw_file_perms;
# 2) 与 tee-supplicant 通信(TA 加载、RPMB、文件代理)
allow hal_hello_default tee_supplicant:unix_stream_socket connectto;
allow hal_hello_default tee_data_file:dir r_dir_perms;
allow hal_hello_default tee_data_file:file r_file_perms;
# 3) HAL 服务注册
add_service(hal_hello_default, hal_hello_service)
# file_contexts(标签定义,类型名按你的树)
/dev/tee0 u:object_r:tee_device:s0
/dev/teepriv0 u:object_r:tee_device:s0
/vendor/lib/optee_armtz(/.*)? u:object_r:tee_data_file:s0
若 TA 目录在
/vendor/lib/optee_armtz,读该目录本身(dir r_dir_perms)和读.ta文件是两条规则,缺一条就avc denied。
8.6 TEE 侧:login method 与身份校验
| login 方式 | 传给 TA 的身份 | 适用 |
|---|---|---|
TEEC_LOGIN_PUBLIC | 无身份(匿名) | 公开能力(如随机数) |
TEEC_LOGIN_USER | 调用进程 uid | 按用户隔离的数据 |
TEEC_LOGIN_GROUP | 调用进程 gid | 按组授权(推荐给 HAL:HAL 用固定 gid) |
TEEC_LOGIN_APPLICATION | CA 的 application identity | 只允许某个受信 CA |
TA 侧校验示例(TEE_GetPropertyAsIdentity):
TEE_Identity id;
TEE_Result res = TEE_GetPropertyAsIdentity(TEE_PROPSET_CURRENT_CLIENT,
TEE_PROPSET_CURRENT_CLIENT, &id);
if (res != TEE_SUCCESS || id.login != TEE_LOGIN_GROUP || id.uuid != kAllowedUuid)
return TEE_ERROR_ACCESS_DENIED;
推荐组合:TEEC_LOGIN_GROUP + HAL 进程固定 gid + TA 校验 gid/uuid,形成"Android 层 SELinux + TEE 层身份"的双重门。
8.7 判定树:一个能力该放哪一层
某个功能需要 TEE 能力?
├─ 是 → 由厂商 HAL 提供 AIDL 接口;app 只拿 normal/dangerous 权限
│ (HAL 域访问 tee_device,TA 校验 login)
└─ 必须让 app 进程自己调 libteec?
├─ 否 → 结束(强烈推荐这条路)
└─ 是 → 该 app 需同时满足:
① 预装(system/vendor 分区)
② 独立 SELinux 域(seapp_contexts + .te + 过 neverallow)
③ 被授予 tee_device 访问
④ TA login 白名单接受它
⑤ 接受"绕过 HAL 校验"的安全评审
8.8 常见坑
| 现象 | 原因 | 处理 |
|---|---|---|
avc: denied { open } scontext=... tcontext=... tee_device | 域没权限 | 在该域加规则,不要全局放开 |
只有 read 被拒、open 通过 | tee_data_file 只有 dir 规则 | 补 file r_file_perms |
加了规则仍 denied | 进程实际不在你以为的域 | ps -Z 看真实 scontext;检查 seapp_contexts 优先级 |
开机 privapp-permissions 报错 | 特权 app 权限未入 allowlist | 补 privapp-permissions-*.xml |
| HAL 起来了但 app 调不到 | 缺 binder_call(app_domain, hal_hello) 或服务名不匹配 | 看 dmesg 的 avc: denied { call } |
想图省事设 permissive | 会掩盖真实权限问题 | 只在调试期临时用,提交前必须移除 |
| vendor 特权 app 权限拿不到 | Android 11+ 对 vendor 特权应用有额外限制 | 按你的版本核对 vendor 特权应用策略 |
8.9 .te 与 neverallow 到底是什么
上面 8.7 提到"独立域 = seapp_contexts + .te + 过 neverallow"。这三样东西经常被混着说,这里拆开讲清楚。
8.9.1 .te = SELinux 策略源文件(Type Enforcement)
# sepolicy/hal_hello_default.te
type hal_hello_default, domain; # ① 定义一个域(type)
type hal_hello_service, service_manager_type;
hal_server_domain(hal_hello_default, hal_hello) # ② 宏:把域加入 hal_hello 的 attribute 家族
hal_client_domain(system_server, hal_hello) # 允许 system_server 调它
allow hal_hello_default tee_device:chr_file rw_file_perms; # ③ 具体许可:谁 对 什么 做什么
一条 allow 的语义固定为三段:源域(scontext)→ 目标类型(tcontext)→ 权限(perm)。缺一段就不是完整规则。
一套策略不只 .te,而是四个文件配合:
| 文件 | 作用 | 你通常要改吗 |
|---|---|---|
*.te | 域/类型/allow 规则 | ✅ 要 |
file_contexts | 给文件打标签(/dev/tee0 → tee_device) | ✅ 要(新设备节点/新 TA 目录) |
seapp_contexts | 进程按条件选域(app → untrusted_app / priv_app …) | 仅当你要开新 app 域 |
mac_permissions.xml | 按签名给 app 分配 seinfo(喂给 seapp_contexts) | 仅当新 seinfo |
目录结构(Treble 三分区):
system/sepolicy/
├── private/ # 平台私有策略 + 绝大多数 neverallow
├── public/ # 对外导出的 type(vendor 才能引用)
├── vendor/ # vendor 策略
└── prebuilts/api/<ver>/ # 各 API 级别快照(兼容性校验)
device/<vendor>/<board>/sepolicy/ # 板级策略(BOARD_VENDOR_SEPOLICY_DIRS)
8.9.2 策略是怎么"编出来"的
关键:neverallow 参与的是编译这一步,不参与运行时判定。
8.9.3 neverallow = 构建期的"禁止断言"
# system/sepolicy/private/...(示意,实际文本随版本不同)
neverallow { appdomain -shell } tee_device:chr_file *;
- 含义:除 shell 外,任何 appdomain 都不允许访问
tee_device; - 它不是给内核执行的规则,而是给
checkpolicy/secilc编译器看的约束; - 只要你的
.te(含宏展开后的结果)推出了被禁止的访问,编译直接失败:
libsepol.report_failure: neverallow on line 123 of system/sepolicy/private/xxx.te
(or line 456 of .../neverallow) violated by
allow untrusted_app tee_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
所以"加 allow 能生效"和"加 allow 能编译过"是两件事:前者是运行时,后者是构建期。neverallow 的作用就是防止 OEM 用一条 allow 把平台安全模型悄悄打穿。
8.9.4 "过 neverallow"的六种做法
| # | 做法 | 说明 |
|---|---|---|
| ① | 换域,而不是硬加权限 | TEE 访问放 hal_xxx_default(HAL 域);这是唯一架构正确的路 |
| ② | 用官方宏,别手写属性 | hal_server_domain / hal_client_domain / app_domain,宏负责把域加入正确的 attribute |
| ③ | 新域不要继承 appdomain | 若必须让预装 app 直连,定义独立域(如 vendor_tee_app),用 type vendor_tee_app, domain;,不要挂到 appdomain 上 |
| ④ | 跨分区引用要导出 | vendor 引用 system 的 tee_device 时,该 type 必须在 system/sepolicy/public/ 可见 |
| ⑤ | 不要改 AOSP 的 neverallow | 会造成 CTS/GTS 与兼容性校验失败;确有必要要走上游评审 |
| ⑥ | 本地先跑编译 | m sepolicy / m vendor_sepolicy.cil,报错行号直接指向被违反的 neverallow |
8.9.5 一个"合法路径"示例(预装 TEE app)
# 1) 新域,不挂 appdomain
type vendor_tee_app, domain;
allow vendor_tee_app tee_device:chr_file rw_file_perms;
# 2) seapp_contexts:只把特定 seinfo 的包切到这个域
# user=_app seinfo=vendor_tee domain=vendor_tee_app type=app_data_file levelFrom=user
# 3) mac_permissions.xml:给该签名分配 seinfo=vendor_tee
即使这样能编译通过,安全评审仍会问"为什么不走 HAL"——因为它绕过了 HAL 的参数校验与限流。
这就是 8.7 判定树把"app 直连 TA"列为最后选项的原因。
8.9.6 本地验证命令
# 只编策略(最快发现 neverallow 冲突)
m sepolicy
m vendor_sepolicy.cil
# 设备上确认某进程真实落在哪个域(最常用来纠正"我以为它在 hal_xxx")
adb shell ps -Z | grep hello
adb shell ls -Z /dev/tee0
# 查某域到底有没有这条权限(需策略工具 sesearch)
sesearch --allow -s hal_hello_default -t tee_device out/.../sepolicy
8.9.7 一句话总结
.te是"我要什么权限"的声明,neverallow是"平台不允许出现什么"的断言。
你写的规则必须同时满足两边——撞上 neverallow 就得改架构(把访问挪到 HAL 域 / 新开域并评审),
而不是"缺什么加什么"。
第五部分 · 适配与演进:版本、性能、TEE 方案对比
9. 版本适配要点(Android 12 → 16)
| 版本 | 与 TA/CA 相关的关键变化 | 你要做的事 |
|---|---|---|
| 12 | 新 HAL 必须 AIDL(HIDL 停止新增);Treble VINTF 强制 | 从第一天就用 aidl_interface + stability: "vintf" |
| 13 | AIDL 版本冻结实践成熟;vendor.api 校验更严 | 提交 aidl_api/ 冻结快照,改接口必须升版本 |
| 14 | AIDL HAL 全面替代 HIDL;GKI 下 vendor 模块化 | TEE 驱动作为 vendor 模块(vendor_dlkm/vendor_boot) |
| 15 | VNDK 逐步下线;支持 16KB 页 | vendor 侧自带 libteec/依赖;native 库按 16KB 对齐 |
| 16 | 16KB 页设备增多;更强调 vendor/system 依赖隔离 | 确认 .so 用 -Wl,-z,max-page-size=16384 构建 |
16KB 页对齐(Android 15+ 设备必须):
cc_binary {
name: "hello_ca",
vendor: true,
ldflags: ["-Wl,-z,max-page-size=16384"],
...
}
VNDK 下线的影响:不要再依赖 /system/lib64 里的库,libteec、libbinder_ndk 等要么在 vendor 侧自带,
要么使用可 vendor 使用的稳定库(NDK/AIDL NDK backend)。
10. 大块数据与性能
| 问题 | 原因 | 做法 |
|---|---|---|
HAL 返回 byte[] 超过 ~1MB 报 TransactionTooLargeException | Binder 事务缓冲按进程共享,约 1MB | 用 SharedMemory(ashmem/memfd)+ ParcelFileDescriptor,或分块 |
| TEE 侧大数据 | SMC 参数只有 4 个槽、每槽有限 | TEEC_AllocateSharedMemory / MEMREF_PARTIAL,注意 4KB/页对齐 |
| 频繁调用开销 | 每次 InvokeCommand 都是 ioctl + SMC | 会话复用(不要每次 OpenSession);批量命令;共享内存常驻 |
| 并发 | 多线程共享同一 session | 用 TEEC_OpenSession 的 TEEC_MULTI_SESSION/每线程独立会话,或 CA 侧加锁 |
11. OP-TEE 与 Trusty 的差异(Android 官方 TEE)
| 维度 | OP-TEE(GP 路线) | Trusty(Google 路线) |
|---|---|---|
| 客户端 API | GP TEEC_*(libteec) | libtrusty(tipc_connect/tipc_send/tipc_read) |
| 设备节点 | /dev/tee0、/dev/teepriv0 | /dev/trusty-ipc-dev0 |
| TA 形态 | <uuid>.ta 动态加载 | 编译进 Trusty 镜像或作为 app 加载(trusty/user/app/...) |
| 安全存储 | supplicant + RPMB/文件系统 | storage 服务代理 + RPMB |
| Android 集成 | 自定义 HAL / vendor 服务 | 通常由 Keymaster/Keymint、Gatekeeper 等官方 HAL 使用 |
| 适配思路 | 换 CA 侧 API 层;TA 入口函数换成 tipc 消息循环 | — |
结论:"Binder 各层适配"的思路两者一致(App → Service → HAL → TEE),差别只在 HAL 里调的是
TEEC_*还是tipc_*。
第六部分 · 排障与速查
12. 常见错误与排障
| 现象 | 最可能原因 | 定位 |
|---|---|---|
TEEC_InitializeContext 返回 0xFFFF0008 | /dev/tee0 不存在 / 驱动没加载 | ls /dev/tee*、dmesg | grep optee |
TEEC_InitializeContext 返回 0xFFFF0001 | SELinux 拒绝 open | avc: denied 日志 |
TEEC_OpenSession 返回 0xFFFF0008 | TA 未安装 / 路径不对 / UUID 不一致 | ls /vendor/lib/optee_armtz/、比对 UUID 字节序 |
OpenSession 返回 0xFFFF0001 | TA 的 TA_FLAGS 限制 / 登录方式不对 | 检查 TA_FLAG_*、TEEC_LOGIN_PUBLIC |
InvokeCommand 返回 0xFFFF0006 | paramTypes 与 TA 期望不一致 | 两侧打印 pt 对比 |
InvokeCommand 返回 0xFFFF000C | 命令号未实现 | 检查 cmd 常量与 TA 的 switch |
service list 里没有 HAL | init 没拉起 / VINTF 不匹配 / 注册名错 | ps -A | grep hello、logcat | grep -i vintf |
| App 调不到 | Framework 服务没加权限或没注册 | dumpsys activity service、SELinux binder 规则 |
| 只有 32 位 CA 能跑、64 位不行 | .so 架构不一致(libteec 32/64 混用) | file hello_ca、getprop ro.product.cpu.abi |
13. 一页速查
边界:App ─binder→ Service ─binder→ HAL ─libteec→ /dev/tee0 ─SMC→ TA
核心 API:TEEC_InitializeContext → OpenSession → InvokeCommand → Close/Finalize
四个参数:VALUE(2×32b) / TEMP_MEMREF / WHOLE_MEMREF / PARTIAL_MEMREF
TA 五入口:Create/DestroyEntryPoint、Open/CloseSessionEntryPoint、InvokeCommandEntryPoint
必查项:/dev/tee0 权限、TA 在 /vendor/lib/optee_armtz、tee-supplicant 在跑、SELinux avc
版本要点:AIDL+VINTF 稳接口、VNDK 下线自带库、16KB 页对齐、TEE 驱动走 vendor 模块
大块数据:TEE 侧用共享内存;binder 侧用 SharedMemory(>1MB 必踩)
4534



被折叠的 条评论
为什么被折叠?



