用户 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 响应,以指示切换是成功还是失败。

示例:小军使用小红的遥控钥匙打开车门后,HAL 会使用小红的用户 ID 回复 INITIAL_USER_INFO 请求。然后,生物识别传感器 ECU 将小军认定为司机,因此用户 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() 方法移除 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
}