# 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
```
