用户 HAL 属性

当前,许多车辆架构在信息娱乐系统之外包含多个用于控制工效学设计(例如座椅设置和车镜调整)的电子控制单元 (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_enabledadb 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 必须通过以下其中一个选项做出响应:
  • Android 设置的默认行为(切换到上次使用的用户,或者创建新用户 [如果这是第一次启动])。
  • 切换到现有用户。
  • 创建新用户(使用名称、标记、系统语言区域等可选属性创建),并切换到该新用户。

注意:如果 HAL 没有响应,默认行为将在超时期限(默认为五 [5] 秒)之后执行,启动也会因此延迟。 如果 HAL 做出了回复,但 Android 系统无法执行相应操作(例如在用户数量已达到上限的情况下),系统会使用默认行为。

例如,默认情况下,Android 系统会启动最后一位活跃用户。如果检测到其他用户的遥控钥匙,ECU 会替换 HAL 属性,且 Android 系统会在启动期间切换为启动该指定用户。

SWITCH_USER
(读取/写入)
此属性会在切换活跃的前台 Android 用户时被调用。 Android 系统或 HAL 均可调用此属性,以请求切换用户。相关的三种工作流如下:
  • 现代。从 CarUserManager 开始切换。
  • 旧版。从 ActivityManager 开始切换。
  • 车辆。由 HAL 调用以请求切换用户。

现代工作流使用两阶段提交方法,以确保 Android 系统和外部 ECU 保持同步。当 Android 启动切换时:

  1. 检查 HAL 以确定是否可以切换用户。

    HAL 会返回 SUCCESSFAILURE,以便让 Android 知道是否继续。

  2. 完成 Android 用户切换。

    Android 会向 HAL 发送 ANDROID_POST_SWITCH 响应,以指示切换是成功还是失败。

HAL 应等到收到 ANDROID_POST_SWITCH 响应之后再更新其状态,以便同步 ECU,或更新其他 HAL 属性。

例如,司机在移动期间尝试在信息娱乐界面中切换 Android 用户。但是,因为汽车座椅的设置与 Android 用户相关联,所以座椅会在用户切换期间移动。这样一来,控制座椅的 ECU 无法确认此次切换,HAL 会返回失败结果,Android 用户切换因此未能完成。

旧版工作流是在切换用户后发送的单向调用(因此 HAL 无法阻止切换)。此工作流只会在启动时(完成初始用户切换之后)调用,或适用于调用 ActivityManager.switchUser()(而不是 CarUserManager.switchUser())的应用。参考 SettingsSystemUI 应用已在使用后者,但如果 OEM 提供自己的“设置”应用来切换用户,他们应更改用法。

例如,如果应用使用 ActivityManager.switchUser() 来切换用户,系统会向 HAL 发送单向调用,以告知用户切换已发生。

车辆工作流源自 HAL,而非 Android 系统:

  1. HAL 请求切换用户。
  2. 系统完成 Android 用户切换。
  3. Android 会向 HAL 发送 ANDROID_POST_SWITCH 响应,以指示切换是成功还是失败。

例如,Bob 使用 Alice 的遥控钥匙打开车门后,HAL 会使用 Alice 的用户 ID 回复 INITIAL_USER_INFO 请求。然后,生物识别传感器 ECU 将 Bob 认定为司机,因此用户 HAL 发送了 SWITCH_USER 请求,以切换用户。

CREATE_USER
(读取/写入)
当使用 CarUserManager.createUser() API 创建新 Android 用户时,Android 系统会调用此属性。

HAL 会返回 SUCCESSFAILURE。如果 HAL 返回“失败”,Android 系统会移除该用户。

例如,司机点按信息娱乐界面图标来创建新 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 用户与身份识别机制(例如遥控钥匙或手机)相关联。此属性也可用于对关联执行 getset 操作。

例如,司机点按信息娱乐界面图标,以便将用于打开车门的遥控钥匙 (KEY_123) 与当前活跃的 Android 用户 (USER_11) 相关联。

辅助程序库

借助 C++ struct,请求和响应消息中使用的所有对象(例如 UserInfoInitialUserInfoRequestInitialUSerInfoResponse 等)都具有高级别表示法,但移除必须被扁平化为标准的 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
}