You will spend a lot of time looking at lines like this:
0x25400501 intensity=3To most people that hex number is meaningless — just an identifier to copy and paste. It is not. It contains four separate facts, and once you can read them, a category of debugging stops requiring documentation.
Why encode anything in the number at all#
A reasonable question: why not just number properties 1, 2, 3?
Because the number has to be understood by two sides that cannot ask each other questions. The car maker's and Google's Android are built by different companies, years apart, and shipped to a vehicle that will not be updated together.
Packing the essential facts into the ID means both sides can agree on what a property is without consulting a shared document that might be out of date.
The four fields#
Now each one in turn.
Field 1 — Group (4 bits)#
SYSTEM = 0x10000000, // Google defined it; every AAOS vehicle agrees
VENDOR = 0x20000000, // a car maker invented it
BACKPORTED = 0x30000000, // a newer SYSTEM property, implemented early
MASK = 0xf0000000,Field 2 — Area (4 bits)#
GLOBAL = 0x01000000, // one value for the whole vehicle
WINDOW = 0x03000000,
MIRROR = 0x04000000,
SEAT = 0x05000000,
DOOR = 0x06000000,
WHEEL = 0x07000000,
MASK = 0x0f000000,This says what kind of thing the property applies to. Speed is GLOBAL — a car
has one speed. Seat heating is SEAT — there is one value per seat.
Field 3 — Type (8 bits)#
STRING = 0x00100000,
BOOLEAN = 0x00200000,
INT32 = 0x00400000,
INT32_VEC = 0x00410000,
INT64 = 0x00500000,
INT64_VEC = 0x00510000,
FLOAT = 0x00600000,
FLOAT_VEC = 0x00610000,
BYTES = 0x00700000,
MIXED = 0x00e00000,
MASK = 0x00ff0000,This is what tells you which array in the value struct to read. Get it wrong and you read zero, silently.
Field 4 — Unique ID (16 bits)#
Sixteen bits — 65,536 possibilities — that you allocate yourself for vendor properties.
Decoding one by hand#
Take 0x25400501. Here is the whole process, step by step.
Start: 0x25400501
Group: 0x25400501 & 0xf0000000 = 0x20000000 → VENDOR
Area: 0x25400501 & 0x0f000000 = 0x05000000 → SEAT
Type: 0x25400501 & 0x00ff0000 = 0x00400000 → INT32
Id: 0x25400501 & 0x0000ffff = 0x00000501 → vendor id 0x0501The shortcut for reading by eye#
You do not have to do the arithmetic. Look at the hex digits in position:
0x 2 5 4 0 0 5 0 1
│ │ └─┴─ type
│ └───── area
└─────── group
└─┴─┴─ unique id (last four digits)- First digit —
1is SYSTEM,2is VENDOR,3is BACKPORTED. - Second digit —
1GLOBAL,5SEAT,6DOOR,3WINDOW,7WHEEL. - Third and fourth — the type.
40is INT32,60is FLOAT,20is BOOLEAN. - Last four — the unique id.
Composing one in code#
When you define a property, write the fields out. Do not paste a magic constant.
object VendorProperties {
// The bit fields, straight from the AIDL enums.
private const val GROUP_VENDOR = 0x2000_0000.toInt()
private const val AREA_GLOBAL = 0x0100_0000
private const val AREA_SEAT = 0x0500_0000
private const val TYPE_INT32 = 0x0040_0000
/** Seat massage intensity, 0-5, one value per seat. */
const val SEAT_MASSAGE_INTENSITY =
GROUP_VENDOR or AREA_SEAT or TYPE_INT32 or 0x0501 // 0x25400501
/** Cabin scent level, 0-3, one value for the whole car. */
const val CABIN_SCENT_LEVEL =
GROUP_VENDOR or AREA_GLOBAL or TYPE_INT32 or 0x0502 // 0x21400502
}Area IDs, one more time#
This is worth repeating in a second place, because it is the most common source of real defects.
ROW_1_LEFT = 0x0001,
ROW_1_CENTER = 0x0002,
ROW_1_RIGHT = 0x0004,
ROW_2_LEFT = 0x0010,
ROW_2_CENTER = 0x0020,
ROW_2_RIGHT = 0x0040,GLOBAL properties still have an area#
A GLOBAL property has exactly one area, and its area ID is 0.
It still needs an area config entry if you want to declare valid minimum and maximum values. Omitting the config means "no declared range", and the platform will not range-check writes for you.
Before defining anything, check it does not exist#
There are several hundred SYSTEM properties already. Reusing one gives you, for free:
- the framework's existing permission mapping
- established unit conventions other apps already expect
- and test coverage
- documentation you did not have to write
# The HAL-side enum — the authoritative list of IDs
grep -i "SEAT_" hardware/interfaces/automotive/vehicle/aidl/android/hardware/automotive/vehicle/VehicleProperty.aidl
# The app-facing catalogue with Javadoc explaining each one
grep -i "SEAT_" packages/services/Car/car-lib/src/android/car/VehiclePropertyIds.javaNext#
Building one end to end — config, permission, security policy, and the app that finally reads it.

