Skip to content

VHAL Deep Dive

Your AAOS app shows vehicle speed as zero, but the instrument cluster reads 60 km/h. Where’s the disconnect?

TL;DR

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.

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 = 0x11600207
GEAR_SELECTION = 0x11400400
HVAC_TEMPERATURE_SET = 0x15600503
DOOR_LOCK = 0x16200001
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

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).

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
Mental Model

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.

Common Gotcha

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.

Adding a property is a cross-team integration, not a one-file change. Typical steps:

Add to vendor extension range (avoid colliding with AOSP reserved IDs):

// Vendor range: 0x2140xxxx – 0x2FFFxxxx (check OEM allocation table)
VENDOR_TRAILER_TOW_STATUS = 0x21401001

Document: type (INT32), areas (GLOBAL), access (READ), change mode (ON_CHANGE).

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 GetProperty handler — read from gateway cache
  • Implement Subscribe — register for signal change callbacks, push events

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).

Apps use standard CarPropertyManager. For OEM-only access, restrict via car_permission and signature permissions.

Terminal window
# AAOS side: read your property
adb shell dumpsys android.hardware.automotive.vehicle.IVehicle/default | grep -i trailer
# QNX side: verify gateway has the signal
slog2info -w | grep -i trailer
Signal 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.

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 <--> ETH
Where You'll See This

Daily 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:

Terminal window
adb logcat -s VhalClient:V VehicleHal:V CAR.HAL:V

On 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.

Connect the Dots

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.

  1. VHAL is AIDL: properties, not raw CAN frames, face Android.
  2. Property = ID + type + area + access mode. Know all four before debugging.
  3. Virtualized split: client in AAOS, server on QNX, gRPC over vsock between them.
  4. Custom properties: define ID, implement server mapping, verify client passthrough, test both sides.
  5. 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?