当前,许多车辆架构在信息娱乐系统之外包含多个用于控制工效学设计(例如座椅设置和车镜调整)的电子控制单元 (ECU)。根据当前的硬件和电源架构,许多 ECU 会在基于 Android 的信息娱乐系统开启之前启动。这些 ECU 可以通过车载硬件抽象层 (VHAL) 与基于 Android 的信息娱乐系统连接。
从 Android 11 开始,Android Automotive OS (AAOS) 为 VHAL 引入了一组新属性,用于通过创建、切换、移除和关联外部配件来识别用户。例如,司机可以借助这些新属性将外部配件(例如遥控钥匙)与他们的 Android 用户配对关联。然后,当司机接近车辆时,ECU 会被唤醒并检测到遥控钥匙。 此 ECU 会向 HAL 发出指示,告知信息娱乐系统应启动哪个 Android 用户,从而缩短司机等待加载其 Android 用户的时间。
启用用户 HAL
如要显式启用用户 HAL 属性,必须确保将系统属性 android.car.user_hal_enabled
设置为 true
。
(此操作也可以在 car.mk
文件中完成,从而省去手动设置。)通过转储 UserHalService
检查 user_hal_enabled=true
是否已启用:
$ adb shell dumpsys car_service --hal UserHalService|grep enabled user_hal_enabled=true
您也可以使用 adb shell
getprop android.car.user_hal_enabled
或 adb logcat
CarServiceHelper *:s
来检查 user_hal_enabled
。如果此属性已停用,您会在 system_server
启动时看到以下消息:
I CarServiceHelper: Not using User HAL
如需手动启用 user_hal_enabled
,请设置 android.car.user_hal_enabled
系统属性并重启 system_server
:
$ adb shell setprop android.car.user_hal_enabled true $ adb shell stop && adb shell start
logcat
输出如下所示:
I CarServiceHelper: User HAL enabled with timeout of 5000ms D CarServiceHelper: Got result from HAL: OK I CarServiceHelper: User HAL returned DEFAULT behavior
用户 HAL 属性
用户生命周期属性
以下属性提供了用户生命周期状态的 HAL 信息,使 Android 系统与外部 ECU 之间的用户生命周期能够同步。这些属性会使用一个请求与响应协议。在此协议中,Android 系统通过设置属性值发出请求,而 HAL 会通过发出属性更改事件进行响应。
注意:如果用户 HAL 受支持,那么必须实现以下所有属性。
HAL 属性 | 说明 |
---|---|
INITIAL_USER_INFO (读取/写入) |
Android 系统通过调用此属性确定在设备从“挂起到 RAM”(STR) 状态启动或恢复时,系统将启动哪个 Android 用户。被调用后,HAL 必须通过以下其中一个选项做出响应:
注意:如果 HAL 没有响应,默认行为将在超时期限(默认为五 [5] 秒)之后执行,启动也会因此延迟。 如果 HAL 做出了回复,但 Android 系统无法执行相应操作(例如在用户数量已达到上限的情况下),系统会使用默认行为。 例如,默认情况下,Android 系统会启动最后一位活跃用户。如果检测到其他用户的遥控钥匙,ECU 会替换 HAL 属性,且 Android 系统会在启动期间切换为启动该指定用户。 |
SWITCH_USER (读取/写入) |
此属性会在切换活跃的前台 Android 用户时被调用。
Android 系统或 HAL 均可调用此属性,以请求切换用户。相关的三种工作流如下:
现代工作流使用两阶段提交方法,以确保 Android 系统和外部 ECU 保持同步。当 Android 启动切换时:
HAL 应等到收到 例如,司机在移动期间尝试在信息娱乐界面中切换 Android 用户。但是,因为汽车座椅的设置与 Android 用户相关联,所以座椅会在用户切换期间移动。这样一来,控制座椅的 ECU 无法确认此次切换,HAL 会返回失败结果,Android 用户切换因此未能完成。
旧版工作流是在切换用户后发送的单向调用(因此 HAL 无法阻止切换)。此工作流只会在启动时(完成初始用户切换之后)调用,或适用于调用
例如,如果应用使用 车辆工作流源自 HAL,而非 Android 系统:
例如,Bob 使用 Alice 的遥控钥匙打开车门后,HAL 会使用 Alice 的用户 ID 回复 |
CREATE_USER (读取/写入) |
当使用 CarUserManager.createUser() API 创建新 Android 用户时,Android 系统会调用此属性。
HAL 会返回 例如,司机点按信息娱乐界面图标来创建新 Android 用户。这会向 HAL 和其他车载子系统发送请求。ECU 会收到关于新建用户的通知。然后,其他子系统和 ECU 会将自己的内部用户 ID 与新建的 Android 用户 ID 相关联。 |
REMOVE_USER (只写) |
使用 CarUserManager.removeUser() API 移除 Android 用户后,Android 系统会调用此属性。
这是单向调用,HAL 不会做出任何响应。 例如,司机在信息娱乐界面中点按以移除现有 Android 用户。系统会通知 HAL,并告知其他车载子系统和 ECU 用户已移除,以便它们移除其内部用户 ID。 |
其他属性
以下是与用户生命周期状态无关的其他属性。 每种属性都可以在不支持用户 HAL 的情况下实现。
HAL 属性 | 说明 |
---|---|
USER_IDENTIFICATION_ASSOCIATION (读取/写入) |
使用此属性可将任何 Android 用户与身份识别机制(例如遥控钥匙或手机)相关联。此属性也可用于对关联执行 get 或 set 操作。
例如,司机点按信息娱乐界面图标,以便将用于打开车门的遥控钥匙 (KEY_123) 与当前活跃的 Android 用户 (USER_11) 相关联。 |
辅助程序库
借助 C++ struct
,请求和响应消息中使用的所有对象(例如 UserInfo
、InitialUserInfoRequest
和 InitialUSerInfoResponse
等)都具有高级别表示法,但移除必须被扁平化为标准的 VehiclePropValue
对象(请参阅以下示例)。为了简化开发,AOSP 中提供了 C++ 辅助程序库,以自动将用户 HAL structs
转换为 VehiclePropValue
(反之亦然)。
示例
INITIAL_USER_INFO
请求示例(首次启动时)
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 }
响应示例(创建管理员用户)
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
类和属性的实际名称略有不同,但整体工作流相同,如下图所示:
图 1. 用户 HAL 属性工作流
现代工作流的请求示例
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) }
现代工作流的响应示例
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 }
现代工作流的切换后响应示例
此响应通常在 Android 切换成功时出现:
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) }
现代工作流的切换后响应
此响应通常在 Android 切换失败时出现:
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) }
旧版工作流的请求示例
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) }
车辆工作流的请求示例
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 }
旧版工作流的切换后响应
此响应通常在 Android 切换成功时出现:
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
请求示例
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) }
响应示例
VehiclePropValue { // flattened from CreateUserResponse prop: 299896585 // CREATE_USER prop.values.int32Values: [0] = 42 // Request ID (must match request) [1] = 3 // CreateUserStatus::SUCCESS }
REMOVE_USER
请求示例
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
设置示例(与用户 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 }