User HAL 属性

许多当前的车辆架构包含信息娱乐系统外部的多个电子控制单元 (ECU),这些电子控制单元控制人体工程学,例如座椅设置和后视镜调整。根据当前的硬件和电源架构,许多 ECU 在基于 Android 的信息娱乐系统启动之前就已启动。这些 ECU 可以通过 车辆硬件抽象层 (VHAL) 与基于 Android 的信息娱乐系统进行交互。

从 Android 11 开始,Android Automotive OS (AAOS) 在 VHAL 上引入了一组新的属性,用于创建、切换、移除外部配件以及将外部配件与用户关联,以识别用户。例如,这些新属性使驾驶员能够将其外部配件(例如钥匙遥控器)与自己的 Android 用户配对绑定。然后,当驾驶员靠近车辆时,ECU 会唤醒并检测钥匙遥控器。此 ECU 会向 HAL 指示信息娱乐系统应启动哪个 Android 用户,从而缩短驾驶员等待加载其 Android 用户的时间。

启用 User HAL

必须显式启用 User 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

User HAL 属性

用户生命周期属性

以下属性为用户生命周期状态提供 HAL 信息,从而实现 Android 系统和外部 ECU 之间的用户生命周期同步。这些属性使用请求和响应协议,其中 Android 系统通过设置属性值发出请求,HAL 通过发出属性更改事件进行响应。

注意:当支持 User HAL 时,必须实现以下所有属性。

HAL 属性 说明
INITIAL_USER_INFO
(读/写)
Android 系统会调用此属性,以确定设备启动或从挂起到 RAM (STR) 恢复时系统启动哪个 Android 用户。调用时,HAL 必须使用以下选项之一进行响应
  • Android 设置的默认行为(切换到上次使用的用户,或者,如果是首次启动,则创建新用户)。
  • 切换到现有用户。
  • 创建新用户(具有可选的名称、标志、系统语言区域等属性)并切换到该新用户。

注意:如果 HAL 未响应,则默认行为是在超时期限(默认情况下为五秒)后执行,这会延迟启动。如果 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 属性。

示例:在行驶过程中,驾驶员尝试在信息娱乐系统 UI 中切换 Android 用户。但是,由于汽车座椅设置与 Android 用户相关联,因此座椅会在用户切换期间移动。因此,控制座椅的 ECU 不会确认切换,HAL 会以失败响应,并且 Android 用户不会切换。

旧式工作流是在用户切换后发送的单向调用(因此 HAL 无法阻止切换)。它仅在启动时(初始用户切换后)或对于调用 ActivityManager.switchUser() 而不是 CarUserManager.switchUser() 的应用调用。参考 SettingsSystemUI 应用已使用后者,但如果 OEM 提供自己的 Settings 应用来切换用户,则 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,因此 User HAL 发送了 SWITCH_USER 请求以切换用户。

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

HAL 以 SUCCESSFAILURE 响应。如果 HAL 以失败响应,则 Android 系统会移除用户。

示例:驾驶员点按信息娱乐系统 UI 图标以创建新的 Android 用户。这会向 HAL 和车辆子系统的其余部分发送请求。ECU 会收到有关新创建用户的通知。然后,其他子系统和 ECU 会将其内部用户 ID 与 Android 用户 ID 关联。

REMOVE_USER
(仅写入)
在移除 Android 用户后(使用 CarUserManager.removeUser() 方法),Android 系统会调用此属性。

这是一个单向调用,无需 HAL 响应。

示例:驾驶员点按以移除信息娱乐系统 UI 中的现有 Android 用户。HAL 会收到通知,其他车辆子系统和 ECU 也会收到用户移除通知,以便他们可以移除其内部用户 ID。

其他属性

以下是与用户生命周期状态无关的其他属性。每个属性都可以在不提供 User HAL 支持的情况下实现。

HAL 属性 说明
USER_IDENTIFICATION_ASSOCIATION
(读/写)
使用此属性可以将任何 Android 用户与身份验证机制(例如钥匙遥控器或手机)相关联。使用同一属性 getset 关联。

示例:驾驶员点按信息娱乐系统 UI 图标以将用于打开车辆的钥匙遥控器 (KEY_123) 与当前活跃的 Android 用户 (USER_11) 关联。

辅助库

请求和响应消息中使用的所有对象(例如 UserInfoInitialUserInfoRequestInitialUSerInfoResponse 等)都使用 C++ struct 具有高级别表示形式,但必须将移除展平为标准 VehiclePropValue 对象(请参见以下示例)。为了便于开发,AOSP 中提供了 C++ 辅助库,用于自动将 User 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

类和属性的实际名称略有不同,但总体工作流相同,如图所示

Workflow

图 1. User 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
}