使用者 HAL 屬性

目前許多車輛架構都包含多個電子控制單元 (ECU),這些 ECU 位於資訊娛樂系統之外,用於控制人體工學,例如座椅設定和後視鏡調整。在啟動 Android 資訊娛樂系統前,許多 ECU 都是以目前的硬體和電源架構為基礎。這些 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 會透過發出屬性變更事件做出回應。

注意:如果支援 User HAL,則必須實作以下所有屬性。

HAL 屬性 說明
INITIAL_USER_INFO
(讀取/寫入)
Android 系統會呼叫這項屬性,以決定系統在裝置啟動或從「Suspend-to-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 提供自己的設定應用程式來切換使用者,則 OEM 應變更用法。

範例:如果應用程式使用 ActivityManager.switchUser() 切換使用者,則會傳送單向呼叫至 HAL,以便通知系統已切換使用者。

車輛工作流程源自 HAL,而非 Android 系統:

  1. HAL 要求使用者切換。
  2. Android 使用者切換作業已完成。
  3. Android 會將 ANDROID_POST_SWITCH 回應傳送至 HAL,用於指出切換成功或失敗。

範例:Bob 使用 Alice 的車鑰開啟車門,HAL 則以 Alice 的使用者 ID 回覆 INITIAL_USER_INFO 要求。接著,生物特徵辨識感應器 ECU 識別出驅動程式為 Bob,因此使用者 HAL 傳送了 SWITCH_USER 要求來切換使用者。

CREATE_USER
(讀取/寫入)
在使用 CarUserManager.createUser() API 建立新 Android 使用者時,Android 系統會呼叫這個屬性。

HAL 會以 SUCCESSFAILURE 回應。如果 HAL 回應失敗,Android 系統會移除該使用者。

範例:駕駛人輕觸 infotainment UI 圖示,建立新的 Android 使用者。這會將要求傳送至 HAL 和車輛子系統的其他子系統。ECU 會收到新建立使用者的通知。其他子系統和 ECU 隨後將他們的內部使用者 ID 與 Android 使用者 ID 建立關聯。

REMOVE_USER
(僅限寫入)
Android 系統會在移除 Android 使用者 (使用 CarUserManager.removeUser() 方法) 後呼叫此屬性。

此為單向呼叫,但 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

類別和屬性的實際名稱略有不同,但整體工作流程皆相同,如圖所示:

工作流程

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

使用者 ID

設定範例 (與使用者 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
}