你在 App 层写了一句 builder.set(CONTROL_AE_MODE, AE_MODE_ON)。这一行代码在 HAL 层最终变成了什么?它经过了哪些环节才到达 ISP 的 AE 算法?
反过来,ISP 处理完一帧,算出了新的曝光值——这个曝光值怎么穿过层层边界,最终让你在 App 的 onCaptureCompleted() 里读到?
本章就是回答这两个问题。

让我们追踪一个具体参数 CONTROL_AE_MODE = ON 从 App 到 HAL 再到 App 的完整旅程。
① App 层:
builder.set(CaptureRequest.CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
↓
CaptureRequest 内部是一个 CameraMetadata 容器
tag = CONTROL_AE_MODE (0x10090001), value = 1 (ON)
② Binder 传输:
CameraMetadata::flatten()
→ 把所有 tag-value 对序列化成连续字节流
→ 跨 Binder 传给 system_server 中的 CameraService
③ CameraService:
字节流 → unflatten() → 恢复成 CameraMetadata 对象
→ 通过 HIDL/AIDL 传给 CameraProvider 进程
又是一次 flatten() → unflatten()
④ HAL 层 (CamX):
camera3_device_t.process_capture_request() 收到
→ camera3_capture_request_t.settings 就是 metadata
→ Session → Pipeline → Node
⑤ IFE Node:
从 metadata 中读取 CONTROL_AE_MODE
发现它设为了 ON → 启动 AE 自动算法
→ IFE 处理时采集 AE Stats,为下一帧算曝光参数
⑤ ISP 处理完一帧:
IFE Node 写回 result metadata:
→ SENSOR_EXPOSURE_TIME = 33ms(AE 算出来的)
→ SENSOR_SENSITIVITY = 200
→ SENSOR_TIMESTAMP = 265793404062(硬件时间戳)
④ Pipeline/Node 汇聚 result:
Node::ProcessResult() → Pipeline::ProcessPipelineResult()
→ Session 收集所有 Pipeline 的 result → 合并成一个 metadata
③ HIDL/Binder 传输:
又一次 flatten() → unflatten() 的往返
camera3_capture_result_t.result 包含合并后的 metadata
② CameraService → App:
CaptureResult 中包含了所有 result metadata
① App:
TotalCaptureResult result = callback.onCaptureCompleted(...);
Long exposureNs = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
// → 33000000 ns (33ms)
每拍一帧照片,metadata 就在这套"来回跑"的流程中走了两遍。理解了这个,你就理解了 HAL3 的双向通信模型。
📊 参考:AOSP 源码
system/media/camera/include/system/camera_metadata.h
CameraMetadata 是一个为跨进程传输优化的 key-value 容器。
Java HashMap:
- 每条 entry 独立分配 → 内存碎片
- 序列化时要遍历所有 key → 慢
- 不保证内存连续性
CameraMetadata:
- 所有 entry 在连续内存块中
- 可以直接 flatten 成字节流 → 跨 Binder 传输
- O(1) 随机访问
- 预分配固定大小 → 避免运行时 realloc
// system/media/camera/include/system/camera_metadata.h
typedef struct camera_metadata {
uint32_t version;
uint32_t entry_count; // 当前条目数
uint32_t entry_capacity; // 最大容量(预分配)
uint32_t data_count; // 数据区已用大小
uint32_t data_capacity; // 数据区容量(预分配)
camera_metadata_entry_t entries[]; // 条目数组
uint8_t data[]; // 数据区(所有 value 实际存这里)
} camera_metadata_t;
内存布局:
┌───────────────────────────────┐
│ metadata 头部 │
│ version / entry_count / ... │
├───────────────────────────────┤
│ 条目索引区 │
│ entry[0]: tag=CONTROL_AE_MODE │ → 指向 data 区偏移量
│ entry[1]: tag=SENSOR_EXP_TIME│
│ ... │
├───────────────────────────────┤
│ 数据区 │
│ [偏移 0] CONTROL_AE_MODE=1 │ ← 实际数据存在这里
│ [偏移 4] EXPOSURE_TIME=33ms │
│ ... │
└───────────────────────────────┘
为什么这样设计?
→ 序列化时,直接把整块内存拷出去就是字节流
→ 反序列化时,把字节流拷进来,修正指针偏移就行
→ 不需要逐个 entry 解析
由 Android 定义,所有设备必须支持。定义在 AOSP 自动生成的 camera_metadata_tags.h 中:
enum {
// 控制类 (0x1009xxxx 范围)
CONTROL_AE_MODE = 0x10090001,
CONTROL_AF_MODE = 0x10090002,
CONTROL_AWB_MODE = 0x10090003,
// 传感器类 (0x100Bxxxx 范围)
SENSOR_EXPOSURE_TIME = 0x100B0001,
SENSOR_SENSITIVITY = 0x100B0002,
// JPEG 类(section = 7,即 0x0007xxxx 范围)
JPEG_QUALITY = 0x00070001,
JPEG_ORIENTATION = 0x00070002,
// ... 总共约 400 个 tag
};
Tag 编号的编码规则:
0x 0002 0001
│ │ └── Index(在 Section 内的序号,低 16 位)
│ └───────── Section(高 16 位:2 = CONTROL, 0B = SENSOR, ...)
└────────────── 整个 32 位即一个 tag(不含 Type)
💡 Type 并不编进 tag 值:byte / int32 / float 等数据类型,存在
camera_metadata_buffer_entry结构体的独立type字段里(与 tag 分开),通过 tag 查表得到。所以不要试图从 tag 数值本身反推 Type。
厂商自定义的 tag。高通定义的 vendor tag 示例:
// chi-cdk 中生成的 vendor tag 定义
#define VENDOR_EIS3_ENABLE 0x80000001 // EIS 开关
#define VENDOR_RECORDING_END_OF_STREAM 0x80000002 // 录像结束标记
Vendor Tag 的注册流程:
① 厂商在代码中定义 tag 名称和 ID
② 实现 get_vendor_tag_ops() 函数(在 camera_module_t 中注册)
③ CameraService 在 Provider 启动时查询 vendor tag 列表
④ CameraService 将 vendor tag 信息传给 CameraCharacteristics
⑤ App 可以通过 CameraCharacteristics 发现并使用 vendor tag
在 App 层使用 vendor tag:
// App 需要自己定义 Key,因为 vendor tag 不在标准 API 中
public static final CaptureRequest.Key<Byte> EIS_MODE =
new CaptureRequest.Key<>(
"org.quic.camera.eis3enable.EISV3Enable", Byte.class);
// 使用和标准 tag 一样的 set 方法
builder.set(EIS_MODE, (byte) 0x01);
如何查看设备上的所有 vendor tag:
adb shell dumpsys media.camera | grep -A 80 "Vendor tags"
# 列出所有已注册的 vendor tag 及其类型
App 发来的 metadata 到了 CamX 内部后,并不是所有 Node 都能看到全部内容。
Session 收到完整的 Request Metadata
│
├── Pipeline 0 (Preview):
│ 只取自己需要的 tag——比如 AE_MODE、EXPOSURE_TIME
│ 不需要 JPEG_QUALITY(那是 Pipeline 1 的事)
│
└── Pipeline 1 (JPEG):
只取自己需要的 tag——比如 JPEG_QUALITY、JPEG_ORIENTATION
不需要 AE_MODE
每个 Node 通过 DependencyUnit 的 hasPropertyDependency 来声明自己需要哪些 metadata(第 14 章有详细解释):
// 伪代码:IFE Node 声明需要的 property
DependencyUnit ifeDependency;
ifeDependency.hasPropertyDependency = TRUE;
ifeDependency.properties = {
{ CONTROL_AE_MODE },
{ SENSOR_EXPOSURE_TIME },
{ SENSOR_SENSITIVITY },
};
// IFE 只等这三个 property,其他的不需要
CamX 用一个 MetadataPool 来管理每帧的 metadata:
// Session 为每个 Pipeline 分配 metadata pool
MetadataPool* pMainPool = CreateMetadataPool(numSlots);
// 每个 slot 可以存一帧的 metadata
// Slot N 对应 request N
// 处理完 Slot N 后,可以复用 Slot N 给新的 request
adb shell dumpsys media.camera | grep -A 100 "Camera 0"
你会看到类似这样的输出:
Camera 0 (BACK):
android.lens.facing: BACK
android.sensor.orientation: 90
android.sensor.info.activeArraySize: (0,0,4032,3024)
android.sensor.info.exposureTimeRange: (100000, 1000000000)
android.jpeg.availableThumbnailSizes: (176,144),(320,240),...
...
这些就是 ChiContext::InitializeStaticMetadataPool() 构建的内容。
# 开启 metadata dump
adb shell setprop persist.vendor.camera.dumpMetadata 1
# 注意:不是所有平台都支持这个 prop,具体看 camxoverridesettings.txt
三个可能:
1. AE/AF 处于自动模式 → 手动值被覆盖(先关掉自动模式)
2. 该参数不属于当前 TEMPLATE → 换 TEMPLATE_MANUAL
3. 该 tag 是 vendor tag → App 层 Key 定义不对
可能原因:
1. 该 tag 只在特定模式下有效(如只在 STILL_CAPTURE 时有值)
2. 该 tag 尚未被任何 Node 填充(HAL 侧没有实现)
3. 该 tag 在 metadata 中被 publish 了但没有写入正确的值
检查:
1. get_vendor_tag_ops() 是否在 camera_module_t 中正确设置
2. ChiContext 初始化时是否注册了 vendor tag
3. dumpsys 中是否有 vendor tag 列表
CameraMetadata = 为跨进程传输优化的 key-value 容器
连续内存设计 → 直接 flatten 成字节流
完整的一次 metadata 旅程:
① App set → Metadata 容器
② flatten → Binder → unflatten(App → system_server)
③ HIDL → 再次 flatten/unflatten(system_server → Provider)
④ HAL 内部: Session → Pipeline → Node(property 依赖过滤)
⑤ ISP 处理完后: Node → Pipeline → Session(汇聚 result)
⑥ 原路返回: HAL → Provider → system_server → App
关键检查点:
dumpsys media.camera → 看 Static Metadata
dumpsys media.camera | grep "Vendor tags" → 看 Vendor Tag
执行 adb shell dumpsys media.camera | grep -A 30 "Camera 0",列出后摄的 5 个 Static Metadata 项及其值。
找到至少两个 Standard Tag 和两个 Vendor Tag。确认它们的 tag ID 前缀(判断 Standard 还是 Vendor)。
在 App 中设置 CONTROL_AE_MODE = OFF 和一个手动曝光时间,然后在 onCaptureCompleted 中读取 SENSOR_EXPOSURE_TIME,确认 HAL 层是否应用了你的设置。