Initial commit

This commit is contained in:
2026-05-08 11:45:15 +02:00
commit e5471b5fa0
120 changed files with 8938 additions and 0 deletions
+268
View File
@@ -0,0 +1,268 @@
# 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
```