사용자 HAL 속성

현재의 많은 차량 아키텍처에는 좌석 설정 및 미러 조정과 같은 인체공학을 제어하는 인포테인먼트 시스템 외부에 여러 전자 제어 장치(ECU)가 포함되어 있습니다. 현재 하드웨어 및 전원 아키텍처에 기반하여 많은 ECU는 Android 기반 인포테인먼트 시스템에 전원이 공급되기 전에 켜집니다. 이러한 ECU는 차량 하드웨어 추상화 계층(VHAL)을 통해 Android 기반 인포테인먼트 시스템과 상호작용할 수 있습니다.

Android 11부터 Android Automotive OS(AAOS)에서는 사용자를 식별하기 위해 외부 액세서리를 생성, 전환, 삭제, 연결하는 VHAL의 새 속성 세트를 도입했습니다. 예를 들어 이러한 새 속성을 사용하면 운전자가 전자 키와 같은 외부 액세서리를 Android 사용자와 페어링할 수 있습니다. 운전자가 차량에 접근하면 ECU의 절전 모드가 해제되고 전자 키를 감지합니다. 이 ECU는 인포테인먼트가 부팅되어야 하는 Android 사용자를 HAL에 알리므로 Android 사용자가 로드되도록 운전자가 기다리는 시간을 줄입니다.

사용자 HAL 사용 설정

사용자 HAL 속성은 시스템 속성 android.car.user_hal_enabledtrue로 설정되도록 하여 명시적으로 사용 설정해야 합니다. 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_enabled 또는 adb 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 시스템에서 호출하여 기기가 Suspend-to-RAM(STR)에서 부팅되거나 다시 시작될 때 시스템이 시작될 Android 사용자를 판단합니다. 속성이 호출되면 HAL은 다음 옵션 중 하나로 응답해야 합니다.
  • Android에서 설정한 기본 동작(마지막으로 사용한 사용자로 전환하거나 첫 번째 부팅인 경우 새 사용자 만들기)입니다.
  • 기존 사용자로 전환합니다.
  • 이름, 플래그, 시스템 언어 등의 선택적 속성을 사용하여 새 사용자를 만들고 새 사용자로 전환합니다.

참고: HAL이 응답하지 않으면 기본 동작이 제한 시간(기본값 5초) 후에 실행되고 이로 인해 부팅이 지연됩니다. HAL이 응답하지만 Android 시스템에서 작업을 실행하지 못하는 경우(예: 최대 사용자 수에 도달한 경우) 기본 동작이 사용됩니다.

예를 들어 기본적으로 Android 시스템은 부팅 시 마지막 활성 사용자에서 시작됩니다. 다른 사용자의 전자 키가 감지되면 ECU는 HAL 속성을 재정의하고 시작할 때 Android 시스템은 지정된 사용자로 시작하도록 전환됩니다.

SWITCH_USER
(읽기/쓰기)
이 속성은 활성 포그라운드 Android 사용자를 전환할 때 호출되고 Android 시스템이나 HAL에서 호출하여 사용자 전환을 요청할 수 있습니다. 3가지 워크플로는 다음과 같습니다.
  • Modern. CarUserManager에서 전환이 시작되었습니다.
  • Legacy. ActivityManager에서 전환이 시작되었습니다.
  • Vehicle. HAL에서 호출하여 사용자 전환을 요청합니다.

Modern 워크플로는 2단계 커밋 접근 방식을 사용하여 Android 시스템과 외부 ECU가 동기화되도록 합니다. Android에서는 다음과 같이 전환을 시작합니다.

  1. HAL을 확인하여 사용자를 전환할 수 있는지 판단합니다.

    HAL은 SUCCESS 또는 FAILURE로 응답하므로 Android는 계속할지 여부를 파악합니다.

  2. Android 사용자 전환을 완료합니다.

    Android에서는 ANDROID_POST_SWITCH 응답을 HAL에 전송하여 전환 성공 또는 실패를 나타냅니다.

HAL은 ECU를 동기화하도록 상태를 업데이트하거나 다른 HAL 속성을 업데이트하는 ANDROID_POST_SWITCH 응답 후까지 기다려야 합니다.

예를 들어 이동 중에 운전자는 인포테인먼트 UI에서 Android 사용자를 전환하려고 합니다. 그러나 자동차 좌석 설정이 Android 사용자에 연결되어 있으므로 사용자를 전환하는 동안 좌석이 이동합니다. 따라서 좌석을 제어하는 ECU가 전환을 확인하지 않고 HAL은 실패로 응답하며 Android 사용자도 전환되지 않습니다.

Legacy 워크플로는 사용자가 전환된 후 전송된 단방향 호출입니다. 따라서 HAL이 전환을 차단할 수 없습니다. 초기 사용자 전환 후 부팅 시에만 또는 CarUserManager.switchUser() 대신 ActivityManager.switchUser()를 호출하는 앱에서만 호출됩니다. 참조 SettingsSystemUI 앱은 이미 후자를 사용하지만 OEM이 사용자를 전환하기 위해 자체 설정 앱을 제공하는 경우 OEM은 사용을 변경해야 합니다.

예를 들어 앱에서 ActivityManager.switchUser()를 사용하여 사용자를 전환하면 단방향 호출이 HAL에 전송되어 사용자 전환이 발생했다고 알립니다.

Vehicle 워크플로는 Android 시스템이 아닌 HAL에서 시작됩니다.

  1. HAL은 사용자 전환을 요청합니다.
  2. 시스템에서는 Android 사용자 전환을 완료합니다.
  3. Android는 ANDROID_POST_SWITCH 응답을 HAL에 전송하여 전환 성공 또는 실패를 나타냅니다.

예를 들어 정민은 윤아의 전자 키를 사용하여 자동차를 열었고 HAL은 윤아의 사용자 ID로 INITIAL_USER_INFO 요청에 응답했습니다. 그러면 생체 인식 센서 ECU가 운전자를 정민으로 식별했으므로 사용자 HAL은 SWITCH_USER 요청을 전송하여 사용자를 전환했습니다.

CREATE_USER
(읽기/쓰기)
이 속성은 CarUserManager.createUser() API를 사용하여 새 Android 사용자가 만들어질 때 Android 시스템에서 호출합니다.

HAL은 SUCCESS 또는 FAILURE로 응답합니다. HAL이 실패로 응답하면 Android 시스템은 사용자를 삭제합니다.

예를 들어 운전자가 인포테인먼트 UI 아이콘을 탭하여 새 Android 사용자를 만듭니다. 그러면 HAL과 나머지 차량 하위 시스템에 요청이 전송됩니다. 새로 만들어진 사용자를 ECU에 알립니다. 그러면 다른 하위 시스템과 ECU는 내부 사용자 ID를 Android 사용자 ID와 연결합니다.

REMOVE_USER
(쓰기 전용)
이 속성은 CarUserManager.removeUser() API로 Android 사용자가 삭제된 후 Android 시스템에서 호출합니다.

단방향 호출로, HAL에서는 응답이 없습니다.

예를 들어 운전자가 인포테인먼트 UI에서 기존 Android 사용자를 삭제하려고 탭합니다. HAL에 삭제 정보가 전달되며 다른 차량 하위 시스템 및 ECU에도 사용자 삭제를 알려주므로 내부 사용자 ID를 삭제할 수 있습니다.

추가 속성

다음은 사용자 수명 주기 상태와 관련이 없는 추가 속성입니다. 각 속성은 사용자 HAL을 지원하지 않고도 구현할 수 있습니다.

HAL 속성 설명
USER_IDENTIFICATION_ASSOCIATION
(읽기/쓰기)
이 속성을 사용하여 Android 사용자를 전자 키나 전화와 같은 식별 메커니즘과 연결합니다. 동일한 이 속성을 사용하여 연결을 get하거나 set합니다.

예를 들어 운전자는 인포테인먼트 UI 아이콘을 탭하여 차량을 여는 데 사용된 전자 키(KEY_123)를 현재 활성 Android 사용자(USER_11)와 연결합니다.

도우미 라이브러리

UserInfo, InitialUserInfoRequest, InitialUSerInfoResponse와 같은 요청 및 응답 메시지에 사용된 모든 객체는 C++ struct를 사용하여 높은 수준의 표현을 보유하지만 삭제는 표준 VehiclePropValue 객체로 평면화되어야 합니다(아래 예 참고). 쉽게 개발할 수 있도록 C++ 도우미 라이브러리가 AOSP에 제공되어 사용자 HAL structsVehiclePropValue로(또는 그 반대로) 자동 변환합니다.

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 속성 워크플로

Modern 워크플로 요청 예

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

Modern 워크플로 응답 예

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
}

Modern 워크플로 전환 후 응답 예

이 응답은 일반적으로 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)
}

Modern 워크플로 전환 후 응답

이 응답은 일반적으로 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)
}

Legacy 워크플로 요청 예

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

Vehicle 워크플로 요청 예

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
}

Legacy 워크플로 전환 후 응답

이 응답은 일반적으로 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
}