Many current vehicle architectures contain multiple electronic control units (ECUs) outside the infotainment system that control ergonomics, such as seat settings and mirror adjustments. Based on current hardware and power architectures, many ECUs power up before the Android-based infotainment system is powered up. These ECUs can interface with an Android-based infotainment system through the Vehicle hardware abstraction layer (VHAL).
Starting in Android 11, Android Automotive OS (AAOS) introduced a new set of properties on the VHAL for creating, switching, removing, and associating external accessories to identify users. For example, these new properties enable a driver to pair-tie an external accessory, such as a key fob, to their Android user. Then, when the driver approaches the vehicle, an ECU wakes up and detects the key fob. This ECU indicates to the HAL which Android user the infotainment should start a boot up, which reduces the time a driver waits for their Android user to load.
Enable the User HAL
The User HAL properties must be explicitly enabled by ensuring the system
property android.car.user_hal_enabled
is set to true
.
(You can do this in the car.mk
file, so that it doesn't need to be
set manually.) Check that user_hal_enabled=true
is enabled by
dumping the UserHalService
:
$ adb shell dumpsys car_service --hal UserHalService|grep enabled user_hal_enabled=true
You can also check user_hal_enabled
by using adb shell
getprop android.car.user_hal_enabled
or adb logcat
CarServiceHelper *:s
. If the property is disabled, a message like the
following is displayed when system_server
starts:
I CarServiceHelper: Not using User HAL
To manually enable user_hal_enabled
, set the
android.car.user_hal_enabled
system property and restart
system_server
:
$ adb shell setprop android.car.user_hal_enabled true $ adb shell stop && adb shell start
The logcat
output appears as follows:
I CarServiceHelper: User HAL enabled with timeout of 5000ms D CarServiceHelper: Got result from HAL: OK I CarServiceHelper: User HAL returned DEFAULT behavior
User HAL properties
User lifecycle properties
The following properties provide the HAL information for user lifecycle states, which enable user lifecycle synchronization between the Android system and an external ECU. These properties use a request and response protocol, in which the Android system makes a request by setting a property value and the HAL responds by issuing a property change event.
Note: When User HAL is supported, all of the following properties must be implemented.
HAL property | Description |
---|---|
INITIAL_USER_INFO (read/write) |
This property is called by the Android system to determine which Android
user the system starts when the device boots or resumes from
Suspend-to-RAM (STR). When called, the HAL must respond with one of
these options:
Note: If the HAL doesn't respond, the default behavior is to execute after a timeout period (five seconds by default), which delays the boot. If the HAL does reply, but the Android system fails to execute the action (for example, if the maximum number of users has been reached), the default behavior is used. Example: By default, the Android system starts in the last active user upon boot. If a key fob for a different user is detected, the ECU overrides the HAL property and, during start up, the Android system switches to start in that specified user. |
SWITCH_USER (read/write) |
This property is called when switching the active foreground Android user.
The property can be called by either the Android system or by the HAL to
request a user switch. The three workflows are:
The Modern workflow uses a two-phase commit approach to ensure the Android system and external ECU are synchronized. When Android initiates the switch:
The HAL should wait until after the Example: While in motion, a driver attempts to switch Android users in the infotainment UI. However, because car seat settings are tied to the Android user, the seat moves during the user switch. Thus, ECU controlling the seats doesn't confirm the switch, the HAL responds with a failure, and the Android user isn't switched.
The Legacy workflow is a one-way call sent after the user is switched
(so the HAL cannot block the switch). It's only called on boot (after the
initial user switch) or for apps that calling
Example: If an app uses
The Vehicle workflow originates from the HAL, not from the Android system:
Example: Bob used Alice's key fob to open the car
and the HAL replied to the |
CREATE_USER (read/write) |
This property is called by the Android system when a new Android user is
created (using the CarUserManager.createUser() API).
The HAL responds with Example: A driver taps an infotainment UI icon to create a new Android user. This sends a request to the HAL and the rest of the vehicle subsystems. ECUs are informed of the newly created user. Other subsystems and ECUs then associate their internal user ID with the Android user ID. |
REMOVE_USER (WRITE only) |
The Android system calls this property after an Android user is
removed (with the CarUserManager.removeUser() method).
This is a one-way call, no response is expected from the HAL. Example: A driver taps to remove an existing Android user in the infotainment UI. The HAL is informed and other vehicle subsystems and ECUs are informed of the user removal so they can remove their internal user ID. |
Additional properties
The following are additional properties, unrelated to user lifecycle states. Each can be implemented without supporting the User HAL.
HAL property | Description |
---|---|
USER_IDENTIFICATION_ASSOCIATION (read/write) |
Use this property to associate any Android user with an identification
mechanism, such as a key fob or phone. Use this same property to
get or set associations.
Example: A driver taps an infotainment UI icon to associate
the key fob used to open the vehicle ( |
Helper libraries
All objects used in the request and response messages (such as
UserInfo
, InitialUserInfoRequest
,
InitialUSerInfoResponse
, and so on) have a high level representation
using C++ struct
, but removal must be flattened into
standard VehiclePropValue
objects (see the examples below). For ease
of development, a
C++
helper library is provided in AOSP to automatically convert User HAL
structs
into a VehiclePropValue
(and vice versa).
Examples
INITIAL_USER_INFO
Request example (on first boot)
VehiclePropValue { // flattened from InitialUserInfoRequest prop: 299896583 // INITIAL_USER_INFO prop.values.int32Values: [0] = 1 // Request ID [1] = 1 // InitialUserInfoRequestType.FIRST_BOOT [2] = 0 // user id of current user [3] = 1 // flags of current user (SYSTEM) [4] = 1 // number of existing users [5] = 0 // existingUser[0].id [6] = 1 // existingUser[0].flags }
Response example (create Admin user)
VehiclePropValue { // flattened from InitialUserInfoResponse prop: 299896583 // INITIAL_USER_INFO prop.values.int32Values: [0] = 1 // Request ID (must match request) [1] = 2 // InitialUserInfoResponseAction.CREATE [2] = -10000 // user id (not used on CREATE) [3] = 8 // user flags (ADMIN) prop.values.stringValue: "en-US||Car Owner" // User locale and user name }
SWITCH_USER
The actual name of the classes and properties differs slightly but the overall workflow is the same, as illustrated in the figure:
Figure 1. User HAL properties workflow.
Modern workflow request example
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896585 // SWITCH_USER prop.values.int32Values: [0] = 42 // Request ID [1] = 2 // SwitchUserMessageType::ANDROID_SWITCH ("modern") [2,3] = 11,0 // target user id (11) and flags (none in this case) [4,5] = 10,8 // current user id (10) and flags (ADMIN) [6] = 3 // number of existing users (0, 10, 11) [7,8] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [9,10] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [11,12] = 11,0 // existingUser[2] (id=11, flags=NONE) }
Modern workflow response example
VehiclePropValue { // flattened from SwitchUserResponse prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = 42 // Request ID (must match request) [1] = 3 // SwitchUserMessageType::VEHICLE_RESPONSE [2] = 1 // SwitchUserStatus::SUCCESS }
Modern workflow post-switch response example
This response typically occurs when an Android switch succeeds:
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = 42 // Request ID (must match "pre"-SWITCH_USER request ) [1] = 5 // SwitchUserMessageType::ANDROID_POST_SWITCH [2,3] = 11,0 // target user id (11) and flags (none in this case) [4,5] = 11,0 // current user id (11) and flags (none in this case) [6] = 3 // number of existing users (0, 10, 11) [7,8] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [9,10] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [11,12] = 11,0 // existingUser[2] (id=11, flags=NONE) }
Modern workflow post-switch response
This response typically occurs when an Android switch fails:
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = 42 // Request ID (must match "pre"-SWITCH_USER request ) [1] = 5 // SwitchUserMessageType::ANDROID_POST_SWITCH [2,3] = 11,0 // target user id (11) and flags (none in this case) [4,5] = 10,8 // current user id (10) and flags (ADMIN) [6] = 3 // number of existing users (0, 10, 11) [7,8] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [9,10] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [11,12] = 11,0 // existingUser[2] (id=11, flags=NONE) }
Legacy workflow request example
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = 2 // Request ID [1] = 1 // SwitchUserMessageType::LEGACY_ANDROID_SWITCH [2,3] = 10,8 // target user id (10) and flags (ADMIN) [4,5] = 0,1 // current user id (0) and flags (SYSTEM) [6] = 3 // number of existing users (0, 10, 11) [7,8] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [9,10] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [11,12] = 11,0 // existingUser[2] (id=11, flags=NONE) }
Vehicle workflow request example
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = -108 // Request ID (must be negative) [1] = 4 // SwitchUserMessageType::VEHICLE_REQUEST [2] = 11 // target user id }
Legacy workflow post-switch response
This response typically occurs when an Android switch succeeds:
VehiclePropValue { // flattened from SwitchUserRequest prop: 299896584 // SWITCH_USER prop.values.int32Values: [0] = -108 // Request ID (must match from vehicle request ) [1] = 5 // SwitchUserMessageType::ANDROID_POST_SWITCH [2,3] = 11,0 // target user id (11) and flags (none in this case) [4,5] = 11,0 // current user id (11) and flags (none in this case) [6] = 3 // number of existing users (0, 10, 11) [7,8] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [9,10] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [11,12] = 11,0 // existingUser[2] (id=11, flags=NONE) }
CREATE_USER
Request example
VehiclePropValue { // flattened from CreateUserRequest prop: 299896585 // CREATE_USER prop.values.int32Values: [0] = 42 // Request ID [1,2] = 11,6 // Android id of the created user and flags (id=11, flags=GUEST, EPHEMERAL) [3,4] = 10,0 // current user id (10) and flags (none in this case) [5] = 3 // number of existing users (0, 10, 11) [6,7] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [8,9] = 10,8 // existingUser[1] (id=10, flags=ADMIN) [10,11] = 11,6 // newUser[2] (id=11, flags=GUEST,EPHEMERAL) }
Response example
VehiclePropValue { // flattened from CreateUserResponse prop: 299896585 // CREATE_USER prop.values.int32Values: [0] = 42 // Request ID (must match request) [1] = 3 // CreateUserStatus::SUCCESS }
REMOVE_USER
Request example
VehiclePropValue { // flattened from RemoveUserRequest prop: 299896586 // REMOVE_USER prop.values.int32Values: [0] = 42 // Request ID [1,2] = 11,0 // Android id of the removed user and flags (none in this case) [3,4] = 10,0 // current user id (10) and flags (none in this case) [5] = 2 // number of existing users (0, 10) [6,7] = 0,1 // existingUser[0] (id=0, flags=SYSTEM) [8,9] = 10,8 // existingUser[1] (id=10, flags=ADMIN) }
USER_IDENTIFICATION_ASSOCIATION
Set example (key fob associated with User 10)
VehiclePropValue { // flattened from UserIdentificationSetRequest prop: 299896587 // USER_IDENTIFICATION_ASSOCIATION prop.values.int32Values: [0] = 43 // Request ID [1,2] = 10,0 // Android id (10) and flags (none in this case) [3] = 1 // number of associations being set [4] = 1 // 1st type: UserIdentificationAssociationType::KEY_FOB [5] = 1 // 1st value: UserIdentificationAssociationSetValue::ASSOCIATE_CURRENT_USER }