이 페이지에서는 SDV 사용자 환경설정 시스템의 아키텍처에 관한 가이드와 사용자 제어 가능 서비스 및 클라이언트를 구현하는 방법을 설명합니다.
아키텍처 개요
사용자 환경설정 시스템은 사용자 설정의 저장 및 관리를 이러한 설정의 시행 및 적용과 분리합니다. 주요 아키텍처 용어는 표에 요약되어 있습니다.
| 기능 | 설명 | 역할 | 책임 |
|---|---|---|---|
| 사용자 제어 가능 서비스 | 특정 도메인 (예: HVAC, 자동차 시트, 오디오)을 관리하는 표준 SDV 서비스 번들 | 제어하는 하드웨어 또는 하위 시스템의 기능과 제약 조건의 의도된 상태를 제공합니다. |
|
| 사용자 환경설정 에이전트 | 중앙 조정자 | 중앙 집중식 스토리지, 사용자 프로필 관리, 알림 허브를 제공합니다. |
|
| 사용자 환경설정 클라이언트 | 사용자 인터페이스를 제공하는 앱 또는 서비스(예: 사용자 환경설정과 상호작용해야 하는 HMI 또는 기타 로직이 있는 IVI 앱) | 사용자와 상호작용하고, 설정을 표시하고, 변경 요청을 시작합니다. |
|
사용자가 제어할 수 있는 서비스 구현
사용자 환경설정 에이전트에게 어떤 키가 있는지, 데이터 유형, 기본값, 제약 조건 (예: 최솟값 및 최댓값)을 알리려면 서비스 번들이 UserPreferencesRegistryService 인터페이스와 상호작용해야 합니다. 서비스가 시작되면 노출하는 설정을 등록해야 합니다. 사용자가 설정을 수정하려고 하거나 사용자를 전환할 때 에이전트가 서비스에서 RequestSettingsChange를 호출합니다.
이 섹션의 단계에 따라 사용자 환경설정 (및 HMI가 있는 사용자)이 서비스를 제어하도록 합니다.
VSIDL 파일에서 서비스 인터페이스를 정의합니다.
- 서버는
com.sdv.google.user_preferences.user_controllable.UserControllableService를 구현해야 합니다. 이렇게 하면 에이전트가 변경 요청을 보낼 수 있습니다. - 클라이언트는
com.sdv.google.user_preferences.UserPreferencesRegistryService를 사용합니다. 이렇게 하면 시작 시 설정이 등록됩니다.
다음 예시에서는 사용자가 제어할 수 있는 서비스의
service_bundle.vsidl를 보여줍니다.service_bundle { name: "MyFeatureService" server { service: "com.sdv.google.user_preferences.user_controllable.UserControllableService" } client { service: "com.sdv.google.user_preferences.UserPreferencesRegistryService" } }- 서버는
시작 시 키 프로토콜
user_preferences_registry_service.proto에 설정을 등록합니다.UserPreferencesRegistryService에 연결합니다.RegisterSettingsRequest인스턴스를 생성합니다.SettingsGroup(설정의 논리적 컬렉션)의 인스턴스를 정의합니다.각 설정에 대해 다음을 정의합니다.
* **Key:** Unique string ID (for example, `TEMPERATURE`) * **Kind:** `PER_USER` (stored per user profile) or `SHARED` (global) * **Default value:** Initial value if no user preference exists * **Constraints:** (optional) Validation rules (for example, Min 16, Max 32 for HVAC).RegisterSettings()를 호출합니다.
다음 예시는 개념적 Rust로 작성되었습니다.
let temperature_setting = SettingDefinition { name: "TEMPERATURE".to_string(), kind: SettingKind::PER_USER.into(), default_value: Value::Int64(22), // Default 22 degrees constraint: Some(Constraints::Int64Constraints(Int64Constraints { min_value: Some(16), max_value: Some(32), ..Default::default() })), ..Default::default() }; registry_client.RegisterSettings(&RegisterSettingsRequest { group_name: "HVAC".to_string(), version: "1.0".to_string(), settings_definitions: vec![temperature_setting], }).await?;user_controllable_service.proto에서 설정 변경 요청을 처리합니다.RequestSettingsChangeRPC를 구현합니다.- 요청된 값이 현재 컨텍스트 내에서 유효한지 확인합니다(예: 하드웨어가 준비되었는지).
- 변경사항을 적용하기 위한 특정 로직을 적용합니다 (예: 좌석 이동, 팬 속도 변경).
RequestSettingsChangeResponse에 적용된 값을 반환합니다.- 변경사항을 수락한 경우 새 값을 반환합니다.
- 값을 거부하거나 제한한 경우 설정한 (또는 유지한) 값을 반환합니다.
다음 예시는 개념적 Rust로 작성되었습니다.
async fn RequestSettingsChange( &self, _caller_id: ServiceFqin, request: &RequestSettingsChangeRequest ) -> SdvResult<RequestSettingsChangeResponse> { let mut applied_settings = Vec::new(); for setting in &request.settings { if self.hardware.set_value(setting.key, setting.value).is_ok() { // Change accepted applied_settings.push(setting.clone()); } else { // Change rejected, return current actual value let current_val = self.hardware.get_value(setting.key); applied_settings.push(create_setting(setting.key, current_val)); } } Ok(RequestSettingsChangeResponse { settings: applied_settings, }) }FactoryResetRPC를 구현하여 하위 시스템을 초기 상태로 되돌립니다.- 모든 설정을 코드 구성에 정의된 기본값으로 재설정합니다.
- 레지스트리 서비스에서
UpdateSettings를 호출하여 값이 외부에서 변경되었음을 에이전트에 알립니다 (사용자 요청이 아닌 재설정에 의해).
하위 호환성 및 스키마 개선
사용자 제어 가능 서비스에 의해 정의된 설정의 스키마는 공개 인터페이스로 간주됩니다. 이 인터페이스는 사용자 환경설정 에이전트와 다양한 클라이언트 (예: HMI)에서 사용하며, 이들은 출시 일정이 다를 수 있습니다. 따라서 시스템 불안정성과 클라이언트 손상을 방지하려면 엄격한 하위 호환성을 유지해야 합니다.
호환 가능한 변경사항
사용자가 제어할 수 있는 서비스는 설정 스키마에 호환되는 변경사항만 도입해야 합니다. 이러한 변경사항을 통해 이전 클라이언트가 업데이트 없이도 계속 올바르게 작동할 수 있습니다.
적절한 기본값이 있는 새로운 선택적 설정을 추가합니다.
- 이 설정은 선택사항이므로 클라이언트는 서비스의 여러 버전을 지원하기 위해 이 설정의 존재 여부를 확인해야 합니다.
- 클라이언트가 이 설정을 인식하도록 업데이트되지 않은 경우 클라이언트는 이 설정을 무시합니다.
- 새 설정에 대한 사용자 환경설정이 없는 경우 에이전트는 제공된 기본값을 사용합니다.
호환되지 않는 변경사항
다음 변경사항은 이전 클라이언트를 즉시 손상시키므로 호환성이 깨지는 것으로 간주되어 금지됩니다.
기존 설정을 삭제합니다.
기존 설정의 의미, 단위 또는 데이터 유형을 변경합니다 (예: 섭씨 온도를 나타내는 정수에서 파스칼의 압력을 나타내는 부동 소수점으로 변경).
기존 설정의 기본값을 변경합니다. 기본값 변경을 금지하는 주요 이유는 지속된 데이터 이전과 관련된 잠재적인 복잡성 때문입니다. 에이전트는 서비스의 등록 스키마에 따라 설정을 저장합니다. 기본값을 변경하려면 기존 사용자 프로필을 모두 새 기본값으로 이전하는 복잡한 논리가 필요하거나 환경설정을 명시적으로 설정하지 않은 사용자에 대해 기술적으로 잘못된 값을 사용할 위험이 있습니다.
필요한 브레이킹 체인지 처리
브레이킹 체인지가 필요한 경우 서비스는 기존 설정을 수정해서는 안 됩니다. 호환성을 유지하면서 이 전환을 관리하는 접근 방식은 주로 사용자가 제어할 수 있는 서비스 로직에 있습니다.
- 원하는 새 정의, 키 또는 단위로 새 설정을 만듭니다.
- 호환성을 위해 등록하여 기존 설정을 지원 중단합니다. 새 클라이언트를 개발할 때는 이 새로운 설정을 사용하세요.
- 새 클라이언트에게 사용자 제어 서비스의 비즈니스 로직 내에서 호환성 로직을 구현하도록 조언합니다.
서비스의 RequestSettingsChange 구현은 한 설정의 변경사항이 지원 중단된 대응 항목에 반영되고 대응 항목의 변경사항이 설정에 반영되도록 해야 합니다.
호환성 로직의 예:
서비스에서 이전 설정
TEMPERATURE_C(섭씨)를 지원 중단하고TEMPERATURE_K(켈빈)을 도입합니다.상담사가 새 설정을 업데이트하는 요청을 보내면 다음이 발생합니다.
서비스는
TEMPERATURE_K(예: 295.15K)에 대한 요청을 수신합니다.서비스의 로직은 이를 섭씨 (22°C)로 변환하고 기존 클라이언트의 일관성을 유지하기 위해 두 값을 모두 내부적으로 업데이트하고 유지합니다.
에이전트가 기존 클라이언트에서 지원 중단된 설정에 대한 요청을 전송하는 경우:
- 서비스는
TEMPERATURE_C(예: 24°C)에 대한 요청을 수신합니다. - 서비스는 이를 켈빈 (297.15K)으로 변환하고 내부적으로 두 값을 모두 업데이트하고 유지합니다.
- 서비스는
이중 쓰기 전략을 사용하면 출시 일정과 관계없이 모든 클라이언트가 일관되고 정확한 데이터를 읽을 수 있으므로 외부 출시 불일치로 인한 중단을 방지할 수 있습니다.
클라이언트 구현
사용자 환경설정 에이전트와 상호작용하는 클라이언트 (예: HMI)를 구현하려면 다음을 충족해야 합니다.
특정 사용자 환경설정 인터페이스와 상호작용하도록 클라이언트 서비스 번들을 정의합니다. VSIDL 파일에서 다음을 실행합니다.
- 서버로서
com.sdv.google.user_preferences.view.ChangeNotifier를 구현하여 에이전트가 설정 변경에 관한 클라이언트 실시간 알림을 보낼 수 있도록 합니다. - 클라이언트로서
com.sdv.google.user_preferences.UserPreferencesManagementService를 사용하여 설정 수정을 요청하고 업데이트를 구독합니다. - 클라이언트로서
com.sdv.google.user_preferences.UserPreferencesAdminService를 사용하여 사용자 프로필을 관리합니다 (예: 생성, 선택, 삭제, 초기화).
다음 예시는 클라이언트의
service_bundle.vsidl파일을 보여줍니다.service_bundle { name: "MyHmiClient" server { service: "com.sdv.google.user_preferences.view.ChangeNotifier" } client { service: "com.sdv.google.user_preferences.UserPreferencesManagementService" } client { service: "com.sdv.google.user_preferences.UserPreferencesAdminService" } }이에 따라 승인 정책을 추가합니다. 사용자 환경설정 에이전트의 통신을 허용하려면 지정된 서버의 클라이언트를 만듭니다. 예를 들면 다음과 같습니다.
server { service: "com.sdv.google.user_preferences.view.ChangeNotifier" allow_all_channels: true } client { service: "com.sdv.google.user_preferences.UserPreferencesManagementService" allow_all_channels: true } client { service: "com.sdv.google.user_preferences.UserPreferencesAdminService" allow_all_channels: true }- 서버로서
UserPreferencesManagementService에서 요청 설정을 변경할 수 있습니다.UserPreferencesManagementService에 연결합니다.SettingsGroupId및 원하는 설정을 지정하여RequestSettingsChangeRequest인스턴스를 만듭니다.- 선택사항입니다.
ChangePersistencePolicy을PERSISTENT_CHANGE(기본값) 또는NON_PERSISTENT_CHANGE로 설정합니다. RequestSettingsChange()를 호출합니다.
다음 예시는 개념적 Rust로 작성되었습니다.
management_client.RequestSettingsChange(&RequestSettingsChangeRequest { settings_group_id: Some(SettingsGroupId { service_fqin: hvac_service_fqin.to_string(), name: "HVAC".to_string(), ..Default::default() }).into(), settings: vec![Setting { key: "TEMPERATURE".to_string(), value: Some(Value::Int64(24)), ..Default::default() }], change_persistence_policy: ChangePersistencePolicy::PERSISTENT_CHANGE.into(), ..Default::default() }).await?;user_preferences_management_service.proto및change_notifier.proto로 설정 변경사항을 구독합니다.- 서비스 번들 내에서
ChangeNotifier인터페이스에서OnSettingsChangeRPC를 구현합니다. 사용자 환경설정 에이전트는 변경이 발생하면 이 메서드를 호출합니다. UserPreferencesManagementService에 연결합니다.- 모니터링할
SettingsGroupId를 지정하는SubscribeToSettingsChangeAndGetSettingsRequest를 만듭니다. - 설정 상태를 반환한 다음
OnSettingsChange구현을 통해 향후 업데이트를 전송하려면SubscribeToSettingsChangeAndGetSettings()를 호출하세요.
ChangeNotifier인터페이스 구현의 다음 예는 개념적 Rust에 있습니다.#[async_trait] impl ChangeNotifier for MyHmiServiceImpl { async fn OnSettingsChange( &self, _caller_id: ServiceFqin, request: &OnSettingsChangeRequest, ) -> SdvResult<OnSettingsChangeResponse> { // Process the active_settings, pending_changes, and persisted_settings // Update your UI or internal state accordingly. info!("Received settings change for group: {}", request.settings_group_id.name); // ... Ok(OnSettingsChangeResponse::new()) } }다음은 개념적 Rust의 구독 예시입니다.
management_client.SubscribeToSettingsChangeAndGetSettings(&SubscribeToSettingsChangeAndGetSettingsRequest { settings_group_id: Some(SettingsGroupId { service_fqin: hvac_service_fqin.to_string(), name: "HVAC".to_string(), ..Default::default() }).into(), ..Default::default() }).await?;- 서비스 번들 내에서
선택사항: 사용자 프로필을 관리하는 클라이언트 (예: 전용 설정 앱)는
user_preferences_admin_service.proto를 사용하여 관리 서비스와 상호작용할 수 있습니다.UserPreferencesAdminService에 연결합니다.CreateUser,SelectUser,DeleteUser,FactoryReset,ListUsers과 같은 RPC를 사용하여 사용자 프로필을 관리합니다.
사용자를 만드는 다음 예시는 개념적 Rust에 있습니다.
admin_service_client.CreateUser(&CreateUserRequest { user: Some(User { id: 1, flags: UserFlags::DRIVER.value(), ..Default::default() }).into(), ..Default::default() }).await?;user_preferences_admin_service.proto를 사용하여 관리자 서비스를 통해 사용 가능한 설정과 사용자를 검색합니다.UserPreferencesAdminService에서ListUsers()를 호출하여 등록된 모든 사용자의 목록을 가져옵니다.UserPreferencesAdminService에서GetUserSettings(user_id)를 호출하여 특정 사용자의 설정을 가져옵니다.
다음은 사용자를 나열하고 설정을 가져오는 개념적 Rust의 예입니다.
// List all users let list_users_response = admin_service_client.ListUsers(&ListUsersRequest::new()).await?; info!("Available users: {:?}", list_users_response.users); // Get settings for a specific user (for example, user with ID 1) if let Some(user_id) = list_users_response.users.first().map(|u| u.id) { let get_settings_response = admin_service_client.GetUserSettings(&GetUserSettingsRequest { user_id, ..Default::default() }).await?; info!("Settings for user {}: {:?}", user_id, get_settings_response.groups); }
흐름 요약
- 클라이언트가 상담사의 관리 및 관리 서비스에 연결하여 시작합니다.
- 클라이언트는 설정 그룹을 검색하고 구독하며 초기 상태를 표시합니다.
- 클라이언트는 에이전트에
RequestSettingsChange를 전송합니다. - 에이전트는 업데이트된 설정과 상태를 사용하여 클라이언트에
OnSettingsChange알림을 전송합니다.
전체 작업 예시는 @samples/user_preferences/v1/의 HMIService를 참고하세요.
에이전트 구현
오케스트레이터에서 실행한 서비스 번들은 사용자 환경설정 에이전트를 구현합니다. 편의를 위해 참조 구현이 제공됩니다.
또한 에이전트용 이 샘플 서비스 번들 user_preferences_sample.vsidl 파일이 제공됩니다.
package: "com.sdv.oem.user_preferences"
service_bundle {
name: "UserPreferencesServiceBundle"
server {
service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
}
server {
service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
}
server {
service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
}
client {
service: "com.sdv.google.user_preferences.view.ChangeNotifier"
}
client {
service: "com.sdv.google.user_preferences.user_controllable.UserControllableService"
}
}
이 구현에서는 생성된 미들웨어를 사용할 수 있으며 사용자 환경설정 에이전트를 구현하는 서비스 번들에서 참조하는 rust_ffi_shared로 컴파일해야 합니다.
sdv_service_bundle_metadata {
# This name must match the agent's Bundle Name
name: "UserPreferencesServiceBundle"
version_number: 1
version_name: "1"
native_library_path: "lib64/<USER-PREFERENCES-FFI-LIB>.so"
orchestration_config_path: "etc/user_preferences_service_bundle/<USER-PREFERENCES-ORCHESTRATION>.textproto"
authorization_policy_path: "etc/user_preferences_service_bundle/permissions.textproto"
}
클라이언트의 승인 정책 .textproto 파일에 필요한 권한이 있어야 합니다.
client {
service: "com.sdv.google.user_preferences.UserPreferencesManagementService"
allow_all_channels: true
}
client {
service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
allow_all_channels: true
}
client {
service: "com.sdv.google.user_preferences.UserPreferencesAdminService"
allow_all_channels: true
}
부록: 주요 개념
이 섹션에서는 몇 가지 주요 개념을 설명합니다.
사용자
각 차량 사용자 (User 클래스)는 환경설정을 저장할 수 있는 계정을 만들 수 있습니다. 사용자 환경설정은 차량 운전자 계정만 지원합니다.
게스트 운전자는 한 번 사용한 후 자동으로 삭제되는 임시 계정을 만들 수 있습니다.
message User {
// Required.
// A unique ID for the user.
int32 id = 1;
// Bit flags that define properties of user. Integer values have to be powers of 2 as they are used as
// a bit mask.
enum UserFlags {
// Due to Protobuff requirement to have the first enum option set to 0,
// Assign 0 to an unused flag
UNSET = 0x0;
// Marks the user as vehicle driver
DRIVER = 0x01;
// Ephemeral users have non-persistent state, once another user is selected
// the profile is deleted automatically
EPHEMERAL = 0x02;
}
// Required.
// Bitmask for the user flags defined above
int32 flags = 2;
}
설정 그룹
설정 그룹 (SettingsGroup 클래스)은 관련 설정을 정리하고 관리합니다.
차량 내의 각 구성 가능한 구성요소는 키-값 쌍으로 표시된 설정 목록으로 구성된 그룹으로 설정을 정의합니다. 예를 들어 전동 시트가 있는 차량은 운전석용 설정 그룹과 앞좌석 승객용 설정 그룹을 정의할 수 있으며, 두 그룹 모두 동일한 이름의 설정을 포함합니다.
message SettingsGroup {
// Required.
// The identifier for this group.
SettingsGroupId id = 1;
// Required.
// The version number of schema used by this setting group.
string version = 2;
// Required.
// The list of settings within the setting group.
repeated Setting settings = 3;
}
설정
설정, 메시지 (Setting 클래스)는 SettingsGroup 내의 단일 설정을 나타냅니다. 이 메시지는 설정의 이름인 키와 여러 유형 중 하나일 수 있는 값으로 구성됩니다.
이 예에서는 설정이 키와 값으로 표현되는 방식을 보여줍니다.
// A key value pair representing a setting within a UserControllableService SettingsGroup.
message Setting {
// Required.
// A name that uniquely identifies a setting within a SettingsGroup.
string key = 1;
// Required.
// New value of the setting.
oneof value {
bool bool = 2;
float float = 3;
int32 int32 = 4;
int64 int64 = 5;
bytes blob = 6;
int32 enum = 7;
}
}
설정 정의
설정 정의 (SettingDefinition 클래스)는 설정 그룹 내 개별 설정의 템플릿 역할을 합니다. 이러한 속성은 설정이 모든 사용자 간에 공유되는지, 각 사용자에게 고유한지, 외부에서 관리되는지 (통과)와 같은 설정의 기본 특성을 지정합니다.
중요한 점은 설정 정의가 최소 및 최대 값, 허용되는 증분 또는 제한된 옵션 집합과 같은 설정 값에 대한 제약 조건도 정의한다는 것입니다. 이러한 제약 조건은 각 설정의 데이터 무결성 및 일관성을 보장합니다.
// The definition of a setting, along with its properties such as type and constraints
message SettingDefinition {
// Required.
SettingKind kind = 1;
// Required.
SettingWithConstraints setting_with_constraints = 2;
}
enum SettingKind {
// A setting which is applied for all vehicle users.
SHARED = 0;
// Store the value of the setting for each vehicle user separately.
PER_USER = 1;
// UserControllableService is fully responsible for the storage of PASSTHROUGH setting.
// User Preferences only notifies UserControllableService when the value is explicitly set by
// user. User Preferences does not attempt to request changes based on user change or service
// registration.
PASSTHROUGH = 2;
};
message SettingWithConstraints {
// Required
Setting setting = 1;
// Required.
// Defines restrictions on the setting's value
oneof constraints {
FloatConstraints float_constraints = 2;
Int32Constraints int32_constraints = 3;
Int64Constraints int64_constraints = 4;
EnumConstraints enum_constraints = 5;
}
}
message Int32Constraints {
// The minimum value that a setting can have.
optional int32 min_value = 1;
// The maximum value that a setting can have.
optional int32 max_value = 2;
// The step by which a setting's value can be increased or decreased.
optional int32 step = 3;
}
message Int64Constraints {
// The minimum value that a setting can have.
optional int64 min_value = 1;
// The maximum value that a setting can have.
optional int64 max_value = 2;
// The step by which a setting's value can be increased or decreased.
optional int64 step = 3;
}
message FloatConstraints {
// The minimum value that a setting can have.
optional float min_value = 1;
// The maximum value that a setting can have.
optional float max_value = 2;
// The step by which a setting's value can be increased or decreased.
optional float step = 3;
}
message EnumConstraints {
// Required.
// List of unique values sorted in ascending order.
repeated int32 possible_values = 1;
}
사용자가 설정을 업데이트할 때 발생하는 이벤트의 예시 시퀀스
이 예에서는 사용자가 Android Automotive OS (AAOS) 차량용 인포테인먼트 (IVI)를 사용하여 설정을 업데이트할 때 발생하는 이벤트 시퀀스를 보여줍니다.
그림 1. 사용자가 설정을 변경할 때 발생하는 이벤트입니다.
차량이 시작될 때의 이벤트 시퀀스 예
이 예에서는 차량이 시작될 때 SDV 사용자 환경설정이 사용자가 선호하는 설정을 적용하는 방법을 보여줍니다.
그림 2. 차량이 시동을 걸 때 발생하는 이벤트입니다.