用户 HAL 属性

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

从 Android 11 开始,Android Automotive OS (AAOS) 在 VHAL 上引入了一组新属性,用于创建、切换、删除和关联外部配件以识别用户。例如,这些新属性使驱动程序能够将外部配件(例如密钥卡)与其 Android 用户配对。然后,当驾驶员接近车辆时,ECU 被唤醒并检测到钥匙扣。该 ECU 向 HAL 指示信息娱乐系统应启动哪个 Android 用户,从而减少驱动程序等待其 Android 用户加载的时间。

启用用户 HAL

必须通过确保系统属性android.car.user_hal_enabled设置为true来显式启用用户 HAL 属性。 (这也可以在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 时,必须实现以下所有属性。

哈尔属性描述
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 用户不会切换。

Legacy 工作流程是在用户切换后发送的单向调用(因此 HAL 无法阻止切换)。它仅在启动时(初始用户切换后)或调用ActivityManager.switchUser()而不是CarUserManager.switchUser()的应用程序调用。参考SettingsSystemUI应用程序已使用后者,但如果 OEM 提供自己的设置应用程序来切换用户,则 OEM 应更改用法。

示例:如果应用程序使用ActivityManager.switchUser()切换用户,则会向 HAL 发送单向调用以通知已发生用户切换。

Vehicle 工作流程源自 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 系统在删除 Android 用户后调用此属性(使用CarUserManager.removeUser()方法)。

这是一个单向调用 — HAL 不会做出任何响应。

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

附加属性

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

哈尔地产描述
USER_IDENTIFICATION_ASSOCIATION
(读/写)
使用此属性将任何 Android 用户与识别机制(例如密钥卡或电话)相关联。使用相同的属性来getset关联。

示例:驾驶员点击信息娱乐 UI 图标,将用于打开车辆的钥匙扣 ( KEY_123 ) 与当前活动的 Android 用户 ( USER_11 ) 相关联。

辅助库

请求和响应消息中使用的所有对象(例如UserInfoInitialUserInfoRequestInitialUSerInfoResponse等)都具有使用 C++ struct的高级表示,但删除必须展平为标准VehiclePropValue对象(请参阅下面的示例)。为了便于开发,AOSP 中提供了一个C++ 帮助程序库,用于自动将 User HAL structs转换为VehiclePropValue (反之亦然)。

例子

初始用户信息

请求示例(首次启动时)

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
}

切换用户

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

工作流程

图 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)
}

创建用户

请求示例

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)
}

用户识别协会

设置示例(与用户 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
}