VHAL Deep Dive
Your AAOS app shows vehicle speed as zero, but the instrument cluster reads 60 km/h. Where’s the disconnect?
VHAL (Vehicle Hardware Abstraction Layer) is the AIDL-defined interface (android.hardware.automotive.vehicle) between Android and vehicle hardware. Data is modeled as properties: each has a VehicleProperty ID, value type, area IDs (which seat, which door), and access mode (read, write, read-write). On Qualcomm virtualized platforms, the VHAL client in AAOS talks to a VHAL server on QNX over gRPC on vsock; the server owns the actual bus connection. Adding a custom property means defining it in the HAL, implementing both client stub and server handler, and mapping it to a CAN/Ethernet signal.
VHAL is the seam where software meets the car. When speed is wrong, HVAC doesn’t respond, or a new sensor needs an Android-facing API, you’re working VHAL. It’s the most crossed boundary in your daily QNX + AAOS workflow.
AIDL-Based Vehicle HAL Interface
Section titled “AIDL-Based Vehicle HAL Interface”Modern AAOS uses AIDL (Android Interface Definition Language) for VHAL, replacing the older HIDL interface. The canonical definition lives in AOSP:
hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/Key AIDL types:
| Type | Purpose |
|---|---|
IVehicle |
Main HAL interface: get/set/subscribe to properties |
VehiclePropValue |
Property ID + area ID + typed value payload |
VehiclePropConfig |
Metadata: access mode, change mode, supported areas |
SubscribeOptions |
Sample rate, flags for event delivery |
The VHAL implementation is registered as a standard Android HAL service. Car Service binds to it at boot via IVehicle/default (or vendor-specific instance name).
Property Model: VehicleProperty, Types, Area IDs
Section titled “Property Model: VehicleProperty, Types, Area IDs”Every piece of vehicle data is a property identified by a VehicleProperty enum value (integer constant):
PERF_VEHICLE_SPEED = 0x11600207GEAR_SELECTION = 0x11400400HVAC_TEMPERATURE_SET = 0x15600503DOOR_LOCK = 0x16200001Value Types
Section titled “Value Types”| Type | AIDL Representation | Example Properties |
|---|---|---|
| BOOLEAN | boolValues |
Wiper on/off, trunk open |
| INT32 | int32Values |
Gear enum, lock state |
| INT64 | int64Values |
Odometer (large counter) |
| FLOAT | floatValues |
Speed, temperature |
| STRING | stringValue |
VIN, software version |
| BYTES | byteValues |
Raw diagnostic payload |
| MIXED | Multiple arrays | Complex structured data |
Area IDs
Section titled “Area IDs”Vehicles aren’t flat. A property applies to specific areas:
| Area Type | Example Areas | Property Example |
|---|---|---|
| GLOBAL (0) | Whole vehicle | PERF_VEHICLE_SPEED, GEAR_SELECTION |
| SEAT | Row 1 left, Row 1 right, Row 2 | HVAC_TEMPERATURE_SET per seat zone |
| DOOR | Front left, front right, rear, hood, trunk | DOOR_LOCK, DOOR_POS |
| WINDOW | Per-window | WINDOW_POS |
| MIRROR | Left, right, center | MIRROR_Z_POS |
| WHEEL | Four corners | TIRE_PRESSURE |
Area IDs are encoded in the property config. When you get(PERF_VEHICLE_SPEED, areaId=0), you get global speed. When you get(HVAC_TEMPERATURE_SET, areaId=SEAT_ROW_1_LEFT), you get that zone’s target temp.
Think of each property as PID + payload type + place (area ID).
Access Modes
Section titled “Access Modes”Each property declares how it can be accessed:
| Access Mode | Meaning | Example |
|---|---|---|
| READ | Android reads only; value originates on vehicle bus | PERF_VEHICLE_SPEED, FUEL_LEVEL |
| WRITE | Android sends commands; no read-back required | One-shot actions (some OEM-specific) |
| READ_WRITE | Android reads current state and sends set commands | HVAC_TEMPERATURE_SET, DOOR_LOCK |
Additionally, change mode defines update behavior:
| Change Mode | Behavior |
|---|---|
| STATIC | Set once, rarely changes |
| ON_CHANGE | Event pushed when value changes |
| CONTINUOUS | Sampled at configured rate |
VHAL properties are labeled pipes between Android and the car. Each pipe has a name tag (VehicleProperty ID), a size (value type), a destination address (area ID), and a one-way or two-way sign (access mode). Car Service is the plumber’s dispatcher — apps request water from a pipe; Car Service opens the right valve.
Virtualized Architecture: Client ↔ Server
Section titled “Virtualized Architecture: Client ↔ Server”On non-virtualized AAOS (older platforms), VHAL talks to vehicle hardware directly via SPI, socket CAN, or Ethernet. On Qualcomm virtualized cockpit platforms, the architecture splits:
sequenceDiagram participant App as AAOS App participant CS as Car Service participant VC as VHAL Client (AAOS HAL) participant VS as VHAL Server (QNX) participant GW as Bus Gateway participant BUS as CAN / Ethernet
App->>CS: CarPropertyManager.get(SPEED) CS->>VC: IVehicle.get(PERF_VEHICLE_SPEED) VC->>VS: gRPC GetProperty over vsock VS->>GW: Read cached signal / request frame GW->>BUS: CAN read (or subscribe) BUS-->>GW: Speed frame GW-->>VS: Parsed value VS-->>VC: VehiclePropValue (float) VC-->>CS: Return value CS-->>App: Speed in m/s
Note over VC,VS: vsock = virtual socket between VMs<br/>gRPC = serialized protobuf messages| Component | Location | Role |
|---|---|---|
| VHAL Client | AAOS guest VM | Implements IVehicle AIDL; forwards to server |
| vsock | Hypervisor virtual device | VM-to-VM socket; no physical network needed |
| gRPC | Over vsock | Structured RPC: Get, Set, Subscribe, property configs |
| VHAL Server | QNX VM | Owns bus connections; maps properties ↔ signals |
| Bus Gateway | QNX process | CAN/Ethernet frame routing, signal database |
Why split? QNX already owns the bus gateway with deterministic timing. Duplicating bus access inside AAOS would violate isolation and create two competing CAN masters.
Latency is real. Every AAOS property read crosses VM boundary (vsock + gRPC + server dispatch). Cached/subscribed properties are fine for UI. Don’t design a closed-loop control through VHAL. Hard RT belongs on QNX or MCU, not across a hypervisor IPC path.
How to Add a Custom Vehicle Property
Section titled “How to Add a Custom Vehicle Property”Adding a property is a cross-team integration, not a one-file change. Typical steps:
1. Define the Property ID
Section titled “1. Define the Property ID”Add to vendor extension range (avoid colliding with AOSP reserved IDs):
// Vendor range: 0x2140xxxx – 0x2FFFxxxx (check OEM allocation table)VENDOR_TRAILER_TOW_STATUS = 0x21401001Document: type (INT32), areas (GLOBAL), access (READ), change mode (ON_CHANGE).
2. Implement VHAL Server (QNX)
Section titled “2. Implement VHAL Server (QNX)”In the QNX VHAL server process:
- Add property config to the config list returned at init
- Map property ID → gateway signal (CAN signal name, scaling factor, offset)
- Implement
GetPropertyhandler — read from gateway cache - Implement
Subscribe— register for signal change callbacks, push events
3. Implement VHAL Client (AAOS)
Section titled “3. Implement VHAL Client (AAOS)”Usually the client is a generic proxy: it forwards all property IDs to the server without per-property code. Verify your client passes through vendor IDs (some builds filter).
4. Expose via Car Service (if needed)
Section titled “4. Expose via Car Service (if needed)”Apps use standard CarPropertyManager. For OEM-only access, restrict via car_permission and signature permissions.
5. Test End-to-End
Section titled “5. Test End-to-End”# AAOS side: read your propertyadb shell dumpsys android.hardware.automotive.vehicle.IVehicle/default | grep -i trailer
# QNX side: verify gateway has the signalslog2info -w | grep -i trailerSignal Scaling: Raw CAN to VHAL Float
CAN signals are rarely human units. A speed signal might be raw * 0.01 km/h. VHAL expects SI units: speed in m/s.
Conversion chain: CAN raw → gateway DBC decode → engineering value → VHAL server unit conversion → AIDL float → Car Service → app.
Document scaling in the signal database. “Speed is wrong by 3.6x” usually means someone forgot km/h ↔ m/s conversion.
VHAL Data Flow Diagram
Section titled “VHAL Data Flow Diagram”flowchart LR subgraph AAOS["AAOS Guest VM"] APP["Apps"] CAR["Car Service"] CLIENT["VHAL Client<br/>(AIDL IVehicle)"] APP --> CAR --> CLIENT end
subgraph IPC["VM Boundary"] VSOCK["vsock"] GRPC["gRPC"] VSOCK --- GRPC end
subgraph QNX["QNX Host / Safety VM"] SERVER["VHAL Server"] GW["Bus Gateway"] DBC["Signal Database<br/>(DBC / ARXML)"] SERVER --> GW --> DBC end
subgraph Vehicle["Vehicle"] CAN["CAN Bus"] ETH["Automotive Ethernet"] end
CLIENT <-->|"Get / Set / Subscribe"| GRPC GRPC <-->|"VehiclePropValue"| SERVER GW <--> CAN GW <--> ETHDaily VHAL debug split by symptom:
| Symptom | Check AAOS | Check QNX |
|---|---|---|
| Property always 0 | dumpsys IVehicle, logcat VhalClient |
VHAL server logs, gateway signal cache |
| Property stale (not updating) | Subscription sample rate, Car Service cache | Gateway RX counters, bus silence |
| Set command ignored | Access mode (READ-only?), permission | Server Set handler, actuator path |
| Intermittent timeouts | vsock connectivity, HAL thread blocked | Server load, gateway priority |
Useful logcat filters:
adb logcat -s VhalClient:V VehicleHal:V CAR.HAL:VOn QNX, the VHAL server process name varies by BSP. Grep slog2 for vhal, vehicle, or your OEM service name.
When integrating a new property, write the signal trace document first: CAN ID → signal name → gateway mapping → VHAL property ID → Car API constant. Future-you will thank present-you.
Module 6.1 (QNX) hosts the VHAL server and gateway. Module 6.2 (AAOS) shows how Car Service consumes VHAL. Module 6.4 (Display & Audio) uses separate HALs but the same virtualization pattern. Module 3 (Signals to Services) explains the CAN signals underneath the gateway.
The short version
Section titled “The short version”- VHAL is AIDL: properties, not raw CAN frames, face Android.
- Property = ID + type + area + access mode. Know all four before debugging.
- Virtualized split: client in AAOS, server on QNX, gRPC over vsock between them.
- Custom properties: define ID, implement server mapping, verify client passthrough, test both sides.
- Don’t put hard RT through VHAL. It’s a display/control API, not a safety loop.
Check Your Understanding
1. In a virtualized Qualcomm cockpit platform, where does the VHAL server that owns the CAN bus connection typically run?
2. What does an area ID specify for a VHAL property?
3. An app needs to read PERF_VEHICLE_SPEED. Which access mode and change mode combination is typical?