Everything the car knows reaches Android through one component. Not a collection of drivers, not a messaging system — a single interface offering a list of named values.
This page explains what that means, why it was designed that way, and the parts that trip people up.
The problem the VHAL solves#
Picture what Android would need to do without it.
A car has an engine computer that broadcasts speed as a two-byte number on a wire called , scaled by 0.01. A different car uses a three-byte number scaled by 0.1, on a different wire, from a different supplier. A third sends it over an entirely different network protocol.
Android cannot possibly know all of that. If it tried, every new vehicle would require changes to Android itself.
What "one property" actually is#
A is one named piece of information. That is genuinely all it is.
Examples of real properties:
| Property | What it holds |
|---|---|
PERF_VEHICLE_SPEED | How fast the car is going, in metres per second |
GEAR_SELECTION | Which gear is selected |
HVAC_TEMPERATURE_SET | The temperature the driver asked for, per seat |
DOOR_LOCK | Whether a specific door is locked |
INFO_VIN | The vehicle identification number |
Each property has:
- an ID — a number identifying it
- a type — is it a whole number, a decimal, text?
- an area — does it apply to the whole car, or to one specific seat or door?
- access rules — can Android read it, write it, or both?
- a change mode — is it fixed, does it change occasionally, or continuously?
The entire interface#
Strip away the code-generation ceremony and the Vehicle HAL offers four things:
| Operation | In plain terms |
|---|---|
getAllPropConfigs() | "What do you support, and what are the rules for each?" |
getValues() | "What is the value of these right now?" |
setValues() | "Please change these to this." |
subscribe() | "Tell me whenever these change." |
That is it. Four operations, one list of values.
Config is the contract#
Here is the part that causes the most confusion for newcomers.
Before anyone reads a value, Android asks the VHAL: what properties do you have? The answer is a list of configs — and that answer is the real interface contract.
parcelable VehiclePropConfig {
int prop; // which property
VehiclePropertyAccess access; // READ, WRITE or READ_WRITE
VehiclePropertyChangeMode changeMode; // STATIC, ON_CHANGE or CONTINUOUS
VehicleAreaConfig[] areaConfigs; // which seats/doors, and valid ranges
float minSampleRate; // CONTINUOUS only
float maxSampleRate; // CONTINUOUS only
}access — who is allowed to do what#
Three choices: READ, WRITE, or READ_WRITE.
The temptation is to declare everything READ_WRITE to keep options open. Do
not. If the car owns a value and Android only observes it, declare READ.
changeMode — how the value behaves over time#
This one has three options and choosing wrong has real consequences.
STATIC — never changes after startup. The vehicle identification number.
The number of doors. Android may read it once and cache it forever.
ON_CHANGE — announced whenever it moves. Gear position. Door open or
closed. Most things are this.
CONTINUOUS — sampled at a rate, like a sensor. Speed. Engine RPM. Fuel
level.
areaConfigs — which seat, and what range#
Many properties exist several times over: one per seat, one per door. The area config says which ones are supported and what values are valid for each.
This brings us to the thing that causes more VHAL bugs than anything else.
Area IDs are bits, not numbers#
An is a set of flags, not a counting number.
The two ways to declare seats, and why they differ#
.areaConfigs = {
{ .areaId = ROW_1_LEFT, .minInt32Value = 0, .maxInt32Value = 5 },
{ .areaId = ROW_1_RIGHT, .minInt32Value = 0, .maxInt32Value = 5 },
}.areaConfigs = {
{ .areaId = ROW_1_LEFT | ROW_1_RIGHT, .minInt32Value = 0, .maxInt32Value = 5 },
}Both are legal. They mean different things.
The first says: the driver and passenger each have their own setting, and you can change one without affecting the other.
The second says: there is one setting, shared, and both seats follow it.
What a value looks like#
When Android reads a property, or receives a change, it gets a VehiclePropValue:
parcelable VehiclePropValue {
long timestamp; // when this value was measured
int areaId; // which seat/door it applies to
int prop; // which property
VehiclePropertyStatus status; // AVAILABLE, UNAVAILABLE or ERROR
RawPropValues value; // the actual number, in one of several arrays
}Two fields here cause real problems.
timestamp is not what most people assume#
It must be CLOCK_BOOTTIME, in nanoseconds — that is, nanoseconds since the
device booted, not since 1970.
status is not decoration#
Three values, and they mean genuinely different things:
AVAILABLE— this is a real measurement, trust it.UNAVAILABLE— the property exists, but there is no meaningful value right now. The sensor is warming up. The subsystem is asleep. The trailer is not attached.ERROR— something failed.
The value union#
RawPropValues is not a clever tagged union — it is a struct containing several
arrays, and the property's declared type tells you which one to read.
| Declared type | Read from |
|---|---|
BOOLEAN, INT32 | int32Values[0] |
INT32_VEC | all of int32Values |
FLOAT | floatValues[0] |
INT64 | int64Values[0] |
BYTES | byteValues |
STRING | stringValue |
MIXED | several at once, described elsewhere |
Where the framework meets it#
On the Android side, exactly one thing talks to the VHAL: CarPropertyService,
inside .
It does four jobs:
- Asks for the config list at startup and remembers it.
- Checks permissions on every app request.
- Opens one subscription per property, no matter how many apps want it.
- Refuses anything the VHAL never declared.
Read the reference implementation#
AOSP ships a complete, working VHAL that the emulator uses. It is the best documentation for this interface that exists.
automotive/vehicle/aidl/implTwo files repay reading:
FakeVehicleHardware— how config and value storage fit together. This is the shape your own implementation will take.DefaultVehicleHal— the request handling and subscription plumbing. You will reuse this rather than rewriting it.
Next#
Decoding a property ID by eye — which turns a lot of debugging into arithmetic.

