Halaman ini memberikan panduan tentang arsitektur sistem Preferensi Pengguna SDV dan petunjuk untuk menerapkan layanan dan klien yang dapat dikontrol pengguna.
Ringkasan arsitektur
Sistem Preferensi Pengguna memisahkan penyimpanan dan pengelolaan setelan pengguna dari penerapan dan aplikasi setelan tersebut. Istilah arsitektur utama diringkas dalam tabel:
| Fitur | Deskripsi | Peran | Tanggung jawab |
|---|---|---|---|
| Layanan yang dapat dikontrol pengguna | Paket layanan SDV standar yang mengelola domain tertentu (misalnya, HVAC, kursi mobil, audio) | Menyediakan status yang dimaksudkan dari kemampuan dan batasan hardware atau subsistem yang dikontrolnya. |
|
| Agen preferensi pengguna | Orchestrator pusat | Menyediakan penyimpanan terpusat, pengelolaan profil pengguna, dan hub notifikasi. |
|
| Klien preferensi pengguna | Aplikasi atau layanan yang menyediakan antarmuka pengguna, misalnya, aplikasi IVI dengan HMI atau logika lain yang perlu berinteraksi dengan preferensi pengguna | Berinteraksi dengan pengguna, menampilkan setelan, dan memulai permintaan perubahan. |
|
Mengimplementasikan layanan yang dapat dikontrol pengguna
Untuk memberi tahu agen Preferensi Pengguna tentang kunci yang ada, jenis datanya, nilai
default, dan batasan (misalnya, nilai minimum dan maksimum), paket layanan
Anda harus berinteraksi dengan antarmuka UserPreferencesRegistryService. Saat
layanan Anda dimulai, layanan tersebut harus mendaftarkan setelan yang dieksposnya. Agen memanggil
RequestSettingsChange di layanan Anda saat pengguna mencoba mengubah setelan
(atau saat beralih pengguna).
Ikuti langkah-langkah di bagian ini untuk mengizinkan Preferensi Pengguna (dan pengguna dengan HMI) mengontrol layanan Anda.
Tentukan antarmuka layanan dalam file VSIDL:
- Sebagai server, terapkan
com.sdv.google.user_preferences.user_controllable.UserControllableService. Cara ini memungkinkan agen mengirimkan permintaan perubahan kepada Anda. - Sebagai klien, gunakan
com.sdv.google.user_preferences.UserPreferencesRegistryService. Tindakan ini mendaftarkan setelan Anda saat startup.
Contoh berikut menunjukkan
service_bundle.vsidluntuk layanan yang dapat dikontrol pengguna:service_bundle { name: "MyFeatureService" server { service: "com.sdv.google.user_preferences.user_controllable.UserControllableService" } client { service: "com.sdv.google.user_preferences.UserPreferencesRegistryService" } }- Sebagai server, terapkan
Mendaftarkan setelan saat startup di proto kunci
user_preferences_registry_service.proto:- Hubungkan ke
UserPreferencesRegistryService. - Buat instance
RegisterSettingsRequest. - Tentukan instance
SettingsGroup(kumpulan logis setelan). Untuk setiap setelan, tentukan:
* **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).Panggil
RegisterSettings().
Contoh berikut ada di Rust konseptual:
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?;- Hubungkan ke
Menangani permintaan perubahan setelan di
user_controllable_service.proto:- Terapkan RPC
RequestSettingsChange. - Validasi apakah nilai yang diminta valid dalam konteks saat ini (misalnya, apakah hardware sudah siap).
- Terapkan logika tertentu untuk menerapkan perubahan (misalnya, pindahkan kursi, ubah kecepatan kipas).
- Menampilkan nilai yang diterapkan di
RequestSettingsChangeResponse: - Jika Anda menerima perubahan, kembalikan nilai baru.
- Jika Anda menolak atau membatasi nilai, tampilkan nilai yang Anda tetapkan (atau pertahankan).
Contoh berikut ada di Rust konseptual:
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, }) }- Terapkan RPC
Terapkan RPC
FactoryResetuntuk mengembalikan subsistem Anda ke status bersih:- Mereset semua setelan ke nilai defaultnya (seperti yang ditentukan dalam konfigurasi kode).
- Panggil
UpdateSettingsdi layanan pendaftaran untuk memberi tahu agen bahwa nilai telah berubah secara eksternal (dengan reset, bukan dengan permintaan pengguna).
Kompatibilitas mundur dan evolusi skema
Skema setelan yang ditentukan oleh layanan yang dapat dikontrol pengguna dianggap sebagai antarmuka publik. Antarmuka ini digunakan oleh agen preferensi pengguna dan berbagai klien (misalnya, HMI), yang mungkin memiliki jadwal rilis yang berbeda. Oleh karena itu, mempertahankan kompatibilitas mundur yang ketat sangat penting untuk mencegah ketidakstabilan sistem dan kerusakan klien.
Perubahan yang kompatibel
Layanan yang dapat dikontrol pengguna hanya boleh memperkenalkan perubahan yang kompatibel pada skema setelannya. Perubahan ini memastikan klien lama terus beroperasi dengan benar tanpa update:
Tambahkan setelan opsional baru dengan nilai default yang wajar:
- Setelan ini bersifat opsional, sehingga klien harus memeriksa keberadaannya untuk mendukung berbagai versi layanan.
- Klien mengabaikan setelan ini jika belum diupdate untuk mengenalinya.
- Jika tidak ada preferensi pengguna untuk setelan baru, agen akan menggunakan nilai default yang diberikan.
Perubahan yang tidak kompatibel
Perubahan berikut dianggap merusak dan dilarang karena langsung membahayakan klien lama:
Hapus setelan yang ada.
Mengubah arti, unit, atau jenis data setelan yang ada (misalnya, mengubah dari bilangan bulat yang merepresentasikan suhu dalam Celsius menjadi float yang merepresentasikan tekanan dalam Pascal).
Mengubah nilai default setelan yang ada. Alasan utama melarang perubahan nilai default adalah potensi komplikasi dengan migrasi data yang dipertahankan. Agen menyimpan setelan berdasarkan skema pendaftaran layanan. Mengubah nilai default akan memerlukan logika yang kompleks untuk memigrasikan semua profil pengguna yang ada ke default baru atau berisiko menggunakan nilai yang secara teknis salah untuk pengguna yang tidak pernah secara eksplisit menetapkan preferensi.
Menangani perubahan yang dapat menyebabkan gangguan yang diperlukan
Jika perubahan yang menyebabkan gangguan diperlukan, layanan tidak boleh mengubah setelan yang ada. Pendekatan untuk mengelola transisi ini sekaligus mempertahankan kompatibilitas terutama terletak di logika layanan yang dapat dikontrol pengguna:
- Buat setelan baru dengan definisi, kunci, atau unit baru yang diinginkan.
- Menghentikan penggunaan setelan yang ada dengan mendaftarkannya untuk kompatibilitas. Gunakan setelan baru ini saat Anda mengembangkan klien baru.
- Menyarankan klien baru untuk menerapkan logika kompatibilitas dalam logika bisnis layanan yang dapat dikontrol pengguna.
Implementasi RequestSettingsChange layanan harus memastikan bahwa perubahan
pada satu setelan akan diterapkan pada setelan yang tidak digunakan lagi, dan perubahan pada
setelan yang tidak digunakan lagi akan diterapkan pada setelan.
Contoh logika kompatibilitas:
Layanan menghentikan penggunaan setelan lama
TEMPERATURE_C(Celsius) dan memperkenalkanTEMPERATURE_K(Kelvin).Saat agen mengirim permintaan untuk memperbarui setelan baru:
Layanan menerima permintaan untuk
TEMPERATURE_K(misalnya, 295,15 K).Logika layanan mengonversi nilai ini ke Celsius (22°C) dan secara internal memperbarui serta mempertahankan kedua nilai untuk menjaga konsistensi bagi klien lama.
Saat agen mengirim permintaan untuk setelan yang tidak digunakan lagi dari klien lama:
- Layanan menerima permintaan untuk
TEMPERATURE_C(misalnya, 24°C). - Layanan mengonversi nilai ini menjadi Kelvin (297,15 K) dan secara internal memperbarui serta mempertahankan kedua nilai.
- Layanan menerima permintaan untuk
Strategi penulisan ganda ini memastikan bahwa semua klien, terlepas dari jadwal rilisnya, membaca data yang konsisten dan akurat, sehingga menghindari kerusakan yang disebabkan oleh ketidakcocokan rilis eksternal.
Mengimplementasikan klien
Untuk menerapkan klien (misalnya, HMI) yang berinteraksi dengan agen User Preferences:
Tentukan paket layanan klien Anda untuk berinteraksi dengan antarmuka Preferensi Pengguna tertentu. Dalam file VSIDL Anda:
- Sebagai server, terapkan
com.sdv.google.user_preferences.view.ChangeNotifieruntuk memungkinkan agen mengirim notifikasi real-time kepada klien Anda tentang perubahan setelan. - Sebagai klien, gunakan
com.sdv.google.user_preferences.UserPreferencesManagementServiceuntuk meminta modifikasi setelan dan berlangganan update. - Sebagai klien, gunakan
com.sdv.google.user_preferences.UserPreferencesAdminServiceuntuk mengelola profil pengguna (misalnya, membuat, memilih, menghapus, mereset ke setelan pabrik).
Contoh berikut menunjukkan file
service_bundle.vsidluntuk klien: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" } }Tambahkan kebijakan otorisasi yang sesuai. Untuk mengizinkan komunikasi bagi agen preferensi pengguna, buat klien untuk server yang ditentukan. Contoh:
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 }- Sebagai server, terapkan
Anda dapat mengubah setelan permintaan di
UserPreferencesManagementService:- Hubungkan ke
UserPreferencesManagementService. - Buat instance
RequestSettingsChangeRequest, dengan menentukanSettingsGroupIddan setelan yang diinginkan. - Opsional. Tetapkan
ChangePersistencePolicykePERSISTENT_CHANGE(default) atauNON_PERSISTENT_CHANGE. - Panggil
RequestSettingsChange().
Contoh berikut ada di Rust konseptual:
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?;- Hubungkan ke
Berlangganan perubahan setelan dengan
user_preferences_management_service.protodanchange_notifier.proto:- Terapkan RPC
OnSettingsChangedari antarmukaChangeNotifierdalam paket layanan Anda. Agen Preferensi Pengguna memanggil metode ini saat terjadi perubahan. - Hubungkan ke
UserPreferencesManagementService. - Buat
SubscribeToSettingsChangeAndGetSettingsRequestyang menentukanSettingsGroupIdyang ingin Anda pantau. - Untuk menampilkan status setelan, lalu mengirimkan pembaruan mendatang melalui
implementasi
OnSettingsChangeAnda, panggilSubscribeToSettingsChangeAndGetSettings().
Contoh berikut tentang penerapan antarmuka
ChangeNotifierada di Rust konseptual:#[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()) } }Contoh berikut tentang berlangganan ada di Rust konseptual:
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?;- Terapkan RPC
Opsional: Klien yang mengelola profil pengguna (misalnya, aplikasi setelan khusus) dapat menggunakan
user_preferences_admin_service.protountuk berinteraksi dengan layanan admin:- Hubungkan ke
UserPreferencesAdminService. - Gunakan RPC seperti
CreateUser,SelectUser,DeleteUser,FactoryReset, danListUsersuntuk mengelola profil pengguna.
Contoh berikut tentang cara membuat pengguna ada di Rust konseptual:
admin_service_client.CreateUser(&CreateUserRequest { user: Some(User { id: 1, flags: UserFlags::DRIVER.value(), ..Default::default() }).into(), ..Default::default() }).await?;- Hubungkan ke
Temukan setelan dan pengguna yang tersedia melalui layanan admin dengan
user_preferences_admin_service.proto:- Dapatkan daftar semua pengguna terdaftar dengan memanggil
ListUsers()diUserPreferencesAdminService. - Dapatkan setelan untuk pengguna tertentu dengan memanggil
GetUserSettings(user_id)diUserPreferencesAdminService.
Contoh berikut tentang mencantumkan pengguna dan mendapatkan setelan ada di Rust konseptual:
// 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); }- Dapatkan daftar semua pengguna terdaftar dengan memanggil
Ringkasan alur
- Klien memulai dan terhubung ke layanan pengelolaan dan administrasi agen.
- Klien menemukan dan berlangganan grup setelan serta menampilkan status awal.
- Klien mengirimkan
RequestSettingsChangeke agen. - Agen mengirimkan notifikasi
OnSettingsChangeke klien dengan setelan dan status yang diperbarui.
Untuk contoh kerja lengkap, lihat HMIService di
@samples/user_preferences/v1/.
Menerapkan agen
Paket layanan yang diluncurkan oleh orkestrator menerapkan agen preferensi pengguna. Untuk mempermudah, implementasi referensi disediakan.
Selain itu, file user_preferences_sample.vsidl paket layanan contoh ini untuk agen disediakan:
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"
}
}
Implementasi dapat menggunakan middleware yang dihasilkan dan harus dikompilasi menjadi rust_ffi_shared yang dirujuk oleh paket layanan yang menerapkan agen preferensi pengguna:
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"
}
File kebijakan otorisasi .textproto klien memerlukan izin yang diperlukan:
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
}
Lampiran: konsep utama
Bagian ini menjelaskan beberapa konsep utama.
Pengguna
Setiap pengguna kendaraan (kelas User) dapat membuat akun tempat mereka dapat menyimpan preferensi mereka. Preferensi Pengguna hanya mendukung akun untuk pengemudi kendaraan.
Pengemudi tamu dapat membuat akun sementara yang otomatis dihapus setelah digunakan satu kali.
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;
}
Grup setelan
Grup setelan (kelas SettingsGroup) mengatur dan mengelola setelan terkait.
Setiap komponen yang dapat dikonfigurasi dalam kendaraan menentukan setelannya sebagai grup,
yang terdiri dari daftar setelan yang ditampilkan sebagai key-value pair. Misalnya,
kendaraan dengan kursi elektrik dapat menentukan grup setelan untuk kursi pengemudi
dan grup lain untuk penumpang depan yang keduanya berisi setelan dengan nama yang sama.
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;
}
Setelan
Setelan, pesan (class Setting), mewakili satu setelan dalam
SettingsGroup. Pesan ini terdiri dari kunci, yang merupakan nama
setelan dan nilai, yang dapat berupa salah satu dari beberapa jenis.
Contoh ini menunjukkan cara setelan direpresentasikan dengan kunci dan nilai:
// 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;
}
}
Menyetel definisi
Definisi setelan (kelas SettingDefinition) berfungsi sebagai template untuk
setelan individual dalam grup setelan. Setelan ini menentukan karakteristik mendasar setelan, seperti apakah setelan dibagikan ke semua pengguna, khusus untuk setiap pengguna, atau dikelola secara eksternal (passthrough).
Yang penting, definisi setelan juga menentukan batasan apa pun pada nilai setelan, seperti nilai minimum dan maksimum, kenaikan yang diizinkan, atau serangkaian opsi yang dibatasi. Batasan ini menghasilkan integritas dan konsistensi data untuk setiap setelan.
// 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;
}
Contoh urutan peristiwa saat pengguna memperbarui setelan
Contoh ini menggambarkan urutan peristiwa yang terjadi saat pengguna menggunakan infotainment dalam kendaraan (IVI) Android Automotive OS (AAOS) untuk memperbarui setelan:
Gambar 1. Peristiwa saat pengguna mengubah setelan.
Contoh urutan peristiwa saat kendaraan dinyalakan
Contoh ini menggambarkan cara Preferensi Pengguna SDV menerapkan setelan pilihan pengguna saat kendaraan dinyalakan:
Gambar 2. Peristiwa saat kendaraan dinyalakan.