269 lines
11 KiB
Markdown
269 lines
11 KiB
Markdown
# Pixel 9a Camera HAL Interface Map
|
||
|
||
> **Generated**: 2026-05-07
|
||
> **Device**: Pixel 9a (tegu), API 36 (Android 16)
|
||
> **Device ID**: 58241JEBF08428
|
||
|
||
---
|
||
|
||
## 1. Architecture Overview
|
||
|
||
### CRITICAL DISCOVERY: Camera HAL is a Separate Process
|
||
|
||
The Pixel 9a camera HAL is **NOT** a shared library loaded by `system_server`. It is a **separate process** running as the `system` user, communicating with `cameraserver` via **AIDL Binder IPC**.
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────┐
|
||
│ App Process │
|
||
│ (Google Camera, Fossify Camera, Instagram, etc.) │
|
||
│ Uses Camera2 API → Binder IPC → cameraserver │
|
||
└────────────────────────┬────────────────────────────────────┘
|
||
│ Camera2 API (Binder)
|
||
┌────────────────────────▼────────────────────────────────────┐
|
||
│ cameraserver (PID 1211) │
|
||
│ Process: /system/bin/cameraserver │
|
||
│ User: cameraserver │
|
||
│ Role: Manages camera devices, routes requests to HAL │
|
||
└────────────────────────┬────────────────────────────────────┘
|
||
│ AIDL Binder IPC (ICameraProvider)
|
||
┌────────────────────────▼────────────────────────────────────┐
|
||
│ Camera Provider HAL Process (PID 994) │
|
||
│ Binary: /apex/com.google.pixel.camera.hal/bin/hw/ │
|
||
│ android.hardware.camera.provider@2.7-service-google│
|
||
│ User: system │
|
||
│ APEX: com.google.pixel.camera.hal │
|
||
│ Interface: ICameraProvider/internal/0 (AIDL v3) │
|
||
│ Role: Implements camera HAL, talks to kernel drivers │
|
||
└────────────────────────┬────────────────────────────────────┘
|
||
│ Kernel drivers (/dev/lwis-*)
|
||
┌────────────────────────▼────────────────────────────────────┐
|
||
│ Camera Hardware │
|
||
│ Sensors, ISP, OIS, Actuator, EEPROM │
|
||
└─────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### Implications for HAL Hooking
|
||
|
||
The traditional "HAL wrapper library loaded by system_server" approach is **WRONG** for Pixel 9a. The correct interception strategies are:
|
||
|
||
1. **Hook the camera provider process** — Inject a library into the provider process via `wrap.` property + `LD_PRELOAD`
|
||
2. **Intercept Binder/AIDL IPC** — Hook the Binder communication between `cameraserver` and the provider
|
||
3. **Replace the APEX binary** — Swap the provider binary with a wrapper (requires APEX modification)
|
||
4. **Hook at cameraserver level** — Intercept in `cameraserver` before requests reach the provider
|
||
|
||
**Recommended approach**: Option 1 (hook the camera provider process via `wrap.` property). The provider process is the most direct interception point.
|
||
|
||
---
|
||
|
||
## 2. AIDL Interface Hierarchy
|
||
|
||
### Interface Chain
|
||
|
||
```
|
||
ICameraProvider (v3)
|
||
└── getCameraIdList() → ["device@1.1/internal/0", ...]
|
||
└── openCamera(deviceId, callback) → ICameraDevice
|
||
└── ICameraDevice (v4)
|
||
└── open(sessionCallback) → ICameraDeviceSession
|
||
└── ICameraDeviceSession (v4)
|
||
├── configureStreams(config) → StreamConfiguration
|
||
├── processCaptureRequest(request) → Status
|
||
├── flush() → Status
|
||
└── close()
|
||
```
|
||
|
||
### Key AIDL Interfaces
|
||
|
||
#### ICameraProvider (android.hardware.camera.provider-V4-ndk.so)
|
||
|
||
```aidl
|
||
interface ICameraProvider {
|
||
CameraStatus[] getCameraIdList(out String[] cameraIds);
|
||
CameraStatus isSetTorchModeSupported(String cameraId, out boolean support);
|
||
CameraStatus openCamera(String cameraId, ICameraDeviceCallback callback,
|
||
out ICameraDevice device);
|
||
CameraStatus setTorchMode(String cameraId, boolean enabled);
|
||
CameraStatus notifyDeviceStateChange(long physicalCameraId, long deviceState);
|
||
}
|
||
```
|
||
|
||
#### ICameraDevice (android.hardware.camera.device-V4-ndk.so)
|
||
|
||
```aidl
|
||
interface ICameraDevice {
|
||
CameraMetadata getCameraCharacteristics();
|
||
int getResourceCost();
|
||
CameraStatus open(ICameraDeviceCallback callback,
|
||
out ICameraDeviceSession session);
|
||
void close();
|
||
}
|
||
```
|
||
|
||
#### ICameraDeviceSession — PRIMARY INTERCEPTION TARGET
|
||
|
||
```aidl
|
||
interface ICameraDeviceSession {
|
||
// ★★★ KEY FUNCTION 1 ★★★
|
||
// Configure output/input streams for the camera
|
||
CameraStatus configureStreams(
|
||
in StreamConfiguration requestedConfiguration,
|
||
out HalStreamConfiguration halConfiguration);
|
||
|
||
// ★★★ KEY FUNCTION 2 ★★★
|
||
// Process a capture request — THIS IS WHERE WE REPLACE FRAMES
|
||
CameraStatus processCaptureRequest(
|
||
in CaptureRequest request,
|
||
out CaptureResultMetadata resultMetadata);
|
||
|
||
// ★★★ KEY FUNCTION 3 ★★★
|
||
CameraStatus flush();
|
||
|
||
void close();
|
||
CameraStatus getSignalStreamMap(out SignalStreamMap streamMap);
|
||
CameraStatus processPhysicalCaptureRequest(
|
||
in PhysicalCaptureRequestInfo physicalRequestInfo,
|
||
out CaptureResultMetadata resultMetadata);
|
||
CameraStatus setRepeatingRequests(in CaptureRequest[] requests, out int32_t sequenceId);
|
||
CameraStatus cancelRepeatingRequest(int32_t sequenceId);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. Camera Device Configuration
|
||
|
||
### Device Mapping
|
||
|
||
| API Device | HAL ID | Facing | Description |
|
||
|---|---|---|---|
|
||
| Device 0 | HAL 2 (Rear) | Back | Main rear camera |
|
||
| Device 0 | HAL 3 (RearWide) | Back | Ultra-wide rear camera |
|
||
| Device 1 | HAL 1 (Front) | Front | Selfie camera |
|
||
|
||
### Stream Configuration Formats
|
||
|
||
From `android.scaler.availableStreamConfigurations`:
|
||
|
||
| Format Code | Format Name | Description | Max Resolution |
|
||
|---|---|---|---|
|
||
| 32 | `HAL_PIXEL_FORMAT_YCBCR_420_888` | Flexible YUV 4:2:0 | 4000×3000 |
|
||
| 35 | `HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED` | GPU/ISP opaque format | 4208×3120 |
|
||
| 36 | `HAL_PIXEL_FORMAT_RAW16` | Raw sensor data | Varies |
|
||
| 37 | `HAL_PIXEL_FORMAT_BLOB` (JPEG) | Compressed JPEG | 4000×3000 |
|
||
|
||
**Most important format for virtual camera**: Format 32 (YUV_420_888) — this is what preview streams use and what we need to feed video frames into.
|
||
|
||
### Buffer Flow
|
||
|
||
```
|
||
1. App calls CameraDevice.createCaptureSession() with Surface targets
|
||
2. cameraserver calls ICameraDeviceSession.configureStreams()
|
||
→ HAL allocates/configures output buffers
|
||
3. App calls CaptureRequest.Builder.addTarget(surface)
|
||
4. cameraserver calls ICameraDeviceSession.processCaptureRequest()
|
||
→ HAL fills buffers with camera sensor data
|
||
→ Buffers are returned to cameraserver → app surfaces
|
||
```
|
||
|
||
**Our interception point**: Step 4 — replace buffer contents with decoded video frames BEFORE returning to cameraserver.
|
||
|
||
---
|
||
|
||
## 4. Key Libraries in APEX
|
||
|
||
### Location: `/apex/com.google.pixel.camera.hal/lib64/`
|
||
|
||
| Library | Purpose | Relevance |
|
||
|---|---|---|
|
||
| `libgooglecamerahal.so` | Main Google Camera HAL implementation | **PRIMARY TARGET** |
|
||
| `libgooglecamerahalutils.so` | HAL utility functions | Supporting |
|
||
| `android.hardware.camera.provider-V4-ndk.so` | AIDL Provider interface stubs | Interface definition |
|
||
| `android.hardware.camera.device-V4-ndk.so` | AIDL Device interface stubs | Interface definition |
|
||
| `android.hardware.camera.common-V1-ndk.so` | Common camera types | Interface definition |
|
||
| `android.hardware.camera.metadata-V3-ndk.so` | Camera metadata handling | Needed for result metadata |
|
||
| `liblyric_hwl.so` | Google Lyric Hardware Layer | Google-specific ISP |
|
||
| `libg3a.so` | 3A algorithms (AE/AWB/AF) | Auto-exposure/whitebalance/focus |
|
||
| `libcamerasuezclient.so` | Suez framework client | Google analytics/telemetry |
|
||
| `libion.so` / `libion_google.so` | ION memory allocator | Buffer allocation |
|
||
| `libdmabufheap.so` | DMA-BUF heap allocator | Buffer sharing |
|
||
| `libyuv.so` | YUV format conversion | Format conversion |
|
||
|
||
---
|
||
|
||
## 5. Interception Strategy
|
||
|
||
### Approach: Hook the Camera Provider Process
|
||
|
||
Since the camera HAL runs as a separate process, we inject our hooking library into that process.
|
||
|
||
#### Option A: wrap. Property + LD_PRELOAD (Recommended)
|
||
|
||
```bash
|
||
# Set via Magisk service.sh at boot:
|
||
setprop wrap.android.hardware.camera.provider@2.7-service-google \
|
||
"LD_PRELOAD=/data/local/camera_magic/libcamera_hook.so"
|
||
```
|
||
|
||
The `wrap.` property tells Android's init to restart the process with LD_PRELOAD.
|
||
|
||
#### Option B: Replace APEX Binary
|
||
|
||
Replace the provider binary with a wrapper that loads the original + our hooks. More invasive but more reliable.
|
||
|
||
### Functions to Hook
|
||
|
||
| Function | Library | Purpose | Hook Strategy |
|
||
|---|---|---|---|
|
||
| `configureStreams` | `libgooglecamerahal.so` | Know stream sizes/formats | Observe and store config |
|
||
| `processCaptureRequest` | `libgooglecamerahal.so` | Replace frames | Fill buffers with video data |
|
||
| `flush` | `libgooglecamerahal.so` | Cleanup | Pass through + cleanup our buffers |
|
||
|
||
### Buffer Replacement Flow
|
||
|
||
```
|
||
1. configureStreams() called → store stream config (resolution, format, buffer count)
|
||
2. Allocate our own video frame buffers (matching HAL format)
|
||
3. processCaptureRequest() called:
|
||
a. Check if virtual camera is enabled (/data/local/camera_magic/config.txt)
|
||
b. If enabled: copy decoded video frame into request's output buffer
|
||
c. If disabled: pass through to original HAL
|
||
d. Return OK status
|
||
4. cameraserver receives buffer → sends to app → app shows virtual video
|
||
```
|
||
|
||
---
|
||
|
||
## 6. SELinux Considerations
|
||
|
||
The camera provider process runs as `system` user with its own SELinux context. Injecting libraries requires:
|
||
|
||
1. SELinux policy allowing `system` process to load libraries from `/data/local/`
|
||
2. Magisk `sepolicy.rule` to add:
|
||
```
|
||
allow hal_camera_server system_data_file:file { read open execute };
|
||
```
|
||
|
||
---
|
||
|
||
## 7. Verification Commands
|
||
|
||
```bash
|
||
# Verify camera provider process
|
||
adb shell ps -A | grep camera.provider
|
||
|
||
# Check AIDL service registration
|
||
adb shell service list | grep camera
|
||
|
||
# View camera service events
|
||
adb shell dumpsys media.camera | head -30
|
||
|
||
# Check stream configurations
|
||
adb shell dumpsys media.camera | grep availableStreamConfigurations
|
||
|
||
# Monitor camera usage
|
||
adb shell dumpsys media.camera | grep "CONNECT\|DISCONNECT"
|
||
|
||
# Check loaded libraries in provider process
|
||
adb shell su -c 'cat /proc/$(pidof android.hardware.camera.provider@2.7-service-google)/maps' | grep camera
|
||
```
|