Przewodnik po architekturze i wdrażaniu preferencji użytkowników

Na tej stronie znajdziesz przewodnik po architekturze systemu preferencji użytkownika SDV oraz instrukcje wdrażania usługi i klienta, którymi może sterować użytkownik.

Omówienie architektury

System preferencji użytkownika oddziela przechowywanie ustawień użytkownika i zarządzanie nimi od egzekwowania i stosowania tych ustawień. Najważniejsze terminy architektoniczne zostały podsumowane w tabeli:

FunkcjaOpisRolaOdpowiedzialność
Usługa, którą użytkownik może kontrolować Standardowy pakiet usług SDV, który zarządza konkretną domeną (np. klimatyzacją, fotelami samochodowymi, dźwiękiem). Określa zamierzony stan możliwości i ograniczeń sprzętu lub podsystemu, którym steruje.
  • Rejestracja: informuje agenta preferencji użytkownika o obsługiwanych ustawieniach (metadane, wartości domyślne, ograniczenia).
  • Egzekwowanie i autonomia: otrzymuje żądania zmiany ustawień, sprawdza je pod kątem aktualnego stanu i zasad bezpieczeństwa oraz stosuje je do sprzętu. Zachowuje pełną autonomię i określa, czy zmiana ustawienia jest akceptowana czy odrzucana.
Agent preferencji użytkownika Centralny aranżer Zapewnia scentralizowane przechowywanie danych, zarządzanie profilami użytkowników i centrum powiadomień.
  • Pamięć: przechowuje ustawienia poszczególnych użytkowników (np. kierowcy lub pasażera).
  • Routing: serwery proxy zmieniają żądania klientów (np. interfejsu HMI) na odpowiednią usługę, którą może kontrolować użytkownik.
  • Powiadomienia: rozsyła zmiany do zainteresowanych stron (widoków lub interfejsów HMI) za pomocą interfejsu ChangeNotifier.
  • Zarządzanie użytkownikami: umożliwia przełączanie użytkowników i stosowanie prawidłowego stanu trwałego do wszystkich zarejestrowanych usług.
Klient ustawień użytkownika Aplikacja lub usługa, która udostępnia interfejs użytkownika, np. aplikacja IVI z interfejsem HMI lub inną logiką, która musi wchodzić w interakcję z ustawieniami użytkownika. Wchodzi w interakcje z użytkownikami, wyświetla ustawienia i inicjuje prośby o zmiany.
  • Wyświetlanie: przedstawia użytkownikowi bieżące ustawienia i ograniczenia.
  • Poproś o zmiany:wysyła do agenta ustawień użytkownika zmiany ustawień zainicjowane przez użytkownika.
  • Otrzymywanie powiadomień: subskrybuje aktualizacje ustawień w czasie rzeczywistym i reaguje na nie.

Wdrażanie usługi, którą użytkownik może kontrolować

Aby poinformować agenta User Preferences o tym, jakie klucze istnieją, jakie są ich typy danych, wartości domyślne i ograniczenia (np. wartości minimalne i maksymalne), pakiet usług musi wchodzić w interakcję z interfejsem UserPreferencesRegistryService. Po uruchomieniu usługa musi zarejestrować udostępniane przez nią ustawienia. Agent wywołuje usługę, gdy użytkownik próbuje zmodyfikować ustawienie (lub gdy przełącza się między użytkownikami).RequestSettingsChange

Aby umożliwić ustawieniom użytkownika (i użytkownikowi z HMI) sterowanie usługą, wykonaj czynności opisane w tej sekcji.

  1. Zdefiniuj interfejsy usług w pliku VSIDL:

    • Jako serwer zaimplementuj com.sdv.google.user_preferences.user_controllable.UserControllableService. Umożliwia to agentowi wysyłanie do Ciebie próśb o zmianę.
    • Klient: konsumuje com.sdv.google.user_preferences.UserPreferencesRegistryService. Spowoduje to zarejestrowanie ustawień podczas uruchamiania.

    Poniższy przykład pokazuje service_bundle.vsidl w przypadku usługi, którą może kontrolować użytkownik:

    service_bundle {
        name: "MyFeatureService"
        server {
            service: "com.sdv.google.user_preferences.user_controllable.UserControllableService"
        }
        client {
            service: "com.sdv.google.user_preferences.UserPreferencesRegistryService"
        }
    }
    
  2. Zarejestruj ustawienia przy uruchamianiu w protokole klucza:user_preferences_registry_service.proto

    1. Połącz z UserPreferencesRegistryService.
    2. Utwórz instancję RegisterSettingsRequest.
    3. Zdefiniuj instancję SettingsGroup (logiczną kolekcję ustawień).
    4. W przypadku każdego ustawienia określ:

      *   **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).
      
    5. Zadzwoń: RegisterSettings()

    Poniższy przykład jest napisany w języku 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?;
    
  3. Obsługuj prośby o zmianę ustawień w user_controllable_service.proto:

    1. Zaimplementuj RPC RequestSettingsChange.
    2. Sprawdź, czy żądane wartości są prawidłowe w bieżącym kontekście (np. czy sprzęt jest gotowy).
    3. Zastosuj konkretną logikę, aby wprowadzić zmianę (np. przesuń fotel, zmień szybkość wentylatora).
    4. Zwróć zastosowane wartości w RequestSettingsChangeResponse:
    5. Jeśli zaakceptujesz zmianę, zwróć nową wartość.
    6. Jeśli wartość została odrzucona lub ograniczona, zwróć ustawioną (lub zachowaną) wartość.

    Poniższy przykład jest napisany w języku 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,
        })
    }
    
  4. Zaimplementuj FactoryReset RPC, aby przywrócić podsystem do czystego stanu:

    1. Zresetuj wszystkie ustawienia do wartości domyślnych (zdefiniowanych w konfiguracji kodu).
    2. Wywołaj funkcję UpdateSettings w usłudze rejestru, aby poinformować agenta, że wartości zostały zmienione zewnętrznie (przez zresetowanie, a nie przez żądanie użytkownika).

Zgodność wsteczna i ewolucja schematu

Schemat ustawień zdefiniowanych przez usługę, którą użytkownik może kontrolować, jest uważany za interfejs publiczny. Ten interfejs jest używany przez agenta preferencji użytkownika i różnych klientów (np. HMI), którzy mogą mieć różne harmonogramy wydań. Dlatego zachowanie ścisłej zgodności wstecznej jest niezbędne, aby zapobiec niestabilności systemu i awariom klientów.

Zgodne zmiany

Usługa, którą użytkownik może kontrolować, powinna wprowadzać do schematu ustawień tylko zgodne zmiany. Te zmiany zapewniają, że starsze klienty nadal działają prawidłowo bez aktualizacji:

  • Dodaj nowe ustawienie opcjonalne z rozsądną wartością domyślną:

    • To ustawienie jest opcjonalne, więc klienci muszą sprawdzać jego obecność, aby obsługiwać różne wersje usługi.
    • Klienci ignorują to ustawienie, jeśli nie zostali zaktualizowani, aby je rozpoznawać.
    • Jeśli nie ma preferencji użytkownika dotyczących nowego ustawienia, agent używa podanej wartości domyślnej.

Niezgodne zmiany

Te zmiany są uznawane za powodujące niezgodność i są zabronione, ponieważ natychmiast powodują problemy u starszych klientów:

  • Usuń istniejące ustawienie.

  • Zmień znaczenie, jednostkę lub typ danych istniejącego ustawienia (np. zmień liczbę całkowitą reprezentującą temperaturę w stopniach Celsjusza na liczbę zmiennoprzecinkową reprezentującą ciśnienie w paskalach).

  • Zmień wartość domyślną istniejącego ustawienia. Głównym powodem zakazu zmiany wartości domyślnej jest potencjalne skomplikowanie migracji zapisanych danych. Agent przechowuje ustawienia na podstawie schematu rejestracji usługi. Zmiana wartości domyślnej wymagałaby skomplikowanej logiki, aby przenieść wszystkie istniejące profile użytkowników na nową wartość domyślną, lub wiązałaby się z ryzykiem używania technicznie nieprawidłowej wartości w przypadku użytkowników, którzy nigdy nie ustawili wyraźnie preferencji.

Obsługa wymaganych zmian powodujących niezgodność

Jeśli wymagana jest zmiana powodująca niezgodność, usługa nie może modyfikować istniejącego ustawienia. Podejście do zarządzania tą zmianą przy zachowaniu zgodności znajduje się głównie w logice usługi kontrolowanej przez użytkownika:

  1. Utwórz nowe ustawienie z odpowiednią nową definicją, kluczem lub jednostką.
  2. Wycofaj dotychczasowe ustawienie, rejestrując je pod kątem zgodności. Używaj tego nowego ustawienia podczas pozyskiwania nowych klientów.
  3. Doradzaj nowym klientom, aby w logice biznesowej usługi, którą użytkownik może kontrolować, wdrażali logikę zgodności.

Implementacja RequestSettingsChange usługi musi zapewniać, że zmiana jednego ustawienia jest odzwierciedlana w jego wycofanym odpowiedniku, a zmiana odpowiednika jest odzwierciedlana w ustawieniu.

Przykładowa logika zgodności:

  1. Usługa wycofuje stare ustawienie TEMPERATURE_C (stopnie Celsjusza) i wprowadza TEMPERATURE_K (stopnie Kelwina).

  2. Gdy agent wyśle prośbę o zaktualizowanie ustawienia new:

    • Usługa otrzymuje żądanie TEMPERATURE_K (np.295, 15 K).

    • Logika usługi przekształca tę wartość na stopnie Celsjusza (22°C) i wewnętrznie aktualizuje oraz zapisuje obie wartości, aby zachować spójność w przypadku starszych klientów.

  3. Gdy pracownik obsługi wyśle z klienta starszego typu prośbę o ustawienie, które zostało wycofane:

    • Usługa otrzymuje żądanie dotyczące TEMPERATURE_C (np. 24°C).
    • Usługa przekształca tę wartość na stopnie Kelwina (297,15 K) i wewnętrznie aktualizuje oraz zapisuje obie wartości.

Ta strategia podwójnego zapisu zapewnia, że wszyscy klienci, niezależnie od harmonogramu wydania, odczytują spójne i dokładne dane, co pozwala uniknąć problemów spowodowanych niezgodnością wydań zewnętrznych.

Implementowanie klienta

Aby zaimplementować klienta (np. HMI), który wchodzi w interakcję z agentem UserPreferences:

  1. Zdefiniuj pakiet usług klienta, aby wchodzić w interakcje z określonymi interfejsami User Preferences. W pliku VSIDL:

    • Jako serwer zaimplementuj interfejs com.sdv.google.user_preferences.view.ChangeNotifier, aby umożliwić agentowi wysyłanie do klienta powiadomień w czasie rzeczywistym o zmianach ustawień.
    • Jako klient używaj com.sdv.google.user_preferences.UserPreferencesManagementService, aby prosić o zmiany ustawień i subskrybować aktualizacje.
    • Jako klient możesz używać com.sdv.google.user_preferences.UserPreferencesAdminService do zarządzania profilami użytkowników (np. tworzenia, wybierania, usuwania i przywracania do ustawień fabrycznych).

    Poniższy przykład przedstawia plik service_bundle.vsidl klienta:

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

    Dodaj odpowiednie zasady autoryzacji. Aby zezwolić na komunikację agenta preferencji użytkownika, utwórz klienta dla określonych serwerów. Przykład:

    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
    }
    
  2. Ustawienia prośby możesz zmienić w UserPreferencesManagementService:

    1. Połącz z UserPreferencesManagementService.
    2. Utwórz instancję RequestSettingsChangeRequest, określając SettingsGroupId i odpowiednie ustawienia.
    3. Opcjonalnie. Ustaw wartość ChangePersistencePolicy na PERSISTENT_CHANGE (domyślnie) lub NON_PERSISTENT_CHANGE.
    4. Zadzwoń: RequestSettingsChange()

    Poniższy przykład jest napisany w języku 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?;
    
  3. Subskrybuj zmiany ustawień za pomocą symboli user_preferences_management_service.protochange_notifier.proto:

    1. Zaimplementuj wywołanie OnSettingsChange RPC z interfejsu ChangeNotifier w pakiecie usług. Agent User Preferences wywołuje tę metodę, gdy nastąpi zmiana.
    2. Połącz z UserPreferencesManagementService.
    3. Utwórz SubscribeToSettingsChangeAndGetSettingsRequest, w którym określisz SettingsGroupId, które chcesz monitorować.
    4. Aby przywrócić stan ustawień, a następnie wysyłać przyszłe aktualizacje za pomocą implementacji OnSettingsChange, wywołaj funkcję SubscribeToSettingsChangeAndGetSettings().

    Poniższy przykład implementacji interfejsu ChangeNotifier jest napisany w języku 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())
        }
    }
    

    Poniższy przykład subskrypcji jest napisany w języku 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?;
    
  4. Opcjonalnie: klienci, którzy zarządzają profilami użytkowników (np. dedykowana aplikacja do ustawień), mogą używać user_preferences_admin_service.proto do interakcji z usługą administracyjną:

    1. Połącz z UserPreferencesAdminService.
    2. Do zarządzania profilami użytkowników używaj wywołań RPC, takich jak CreateUser, SelectUser, DeleteUser, FactoryResetListUsers.

    Ten przykład tworzenia użytkownika jest napisany w języku Rust:

    admin_service_client.CreateUser(&CreateUserRequest {
        user: Some(User {
            id: 1,
            flags: UserFlags::DRIVER.value(),
            ..Default::default()
        }).into(),
        ..Default::default()
    }).await?;
    
  5. Odkrywaj dostępne ustawienia i użytkowników za pomocą usługi administracyjnej z user_preferences_admin_service.proto:

    1. Aby uzyskać listę wszystkich zarejestrowanych użytkowników, wywołaj ListUsers() na UserPreferencesAdminService.
    2. Pobierz ustawienia konkretnego użytkownika, wywołując funkcję GetUserSettings(user_id)UserPreferencesAdminService.

    Poniższy przykład wyświetlania użytkowników i pobierania ustawień jest napisany w języku 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);
    }
    

Podsumowanie procesu

  1. Klient uruchamia usługi zarządzania i administracyjne agenta i się z nimi łączy.
  2. Klient wykrywa grupy ustawień i subskrybuje je, a następnie wyświetla stan początkowy.
  3. Klient wysyła do agenta RequestSettingsChange.
  4. Agent wysyła do klienta powiadomienie OnSettingsChange ze zaktualizowanymi ustawieniami i stanem.

Pełny przykład znajdziesz w HMIService@samples/user_preferences/v1/.

Wdrażanie agenta

Pakiet usług uruchomiony przez orkiestratora implementuje agenta preferencji użytkownika. Dla wygody użytkowników udostępniamy implementację referencyjną.

Dodatkowo udostępniamy ten przykładowy pakiet usług user_preferences_sample.vsidl dla agenta:

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

Implementacja może korzystać z wygenerowanego oprogramowania pośredniczącego i powinna być kompilowana do pliku rust_ffi_shared, do którego odwołuje się pakiet usług implementujący agenta preferencji użytkownika:

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

Plik zasad autoryzacji.textproto klienta wymaga niezbędnych uprawnień:

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
}

Dodatek: kluczowe pojęcia

W tej sekcji opisujemy kilka kluczowych pojęć.

Użytkownicy

Każdy użytkownik pojazdu (User klasa) może utworzyć konto, na którym będzie przechowywać swoje preferencje. Ustawienia użytkownika obsługują konta tylko w przypadku kierowców pojazdów. Kierowcy goście mogą tworzyć konta tymczasowe, które są automatycznie usuwane po jednorazowym użyciu.

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

Grupy ustawień

Grupy ustawień (klasa SettingsGroup) służą do porządkowania powiązanych ustawień i zarządzania nimi. Każdy konfigurowalny komponent w pojeździe definiuje swoje ustawienia jako grupy, które składają się z listy ustawień przedstawionych jako pary klucz-wartość. Na przykład pojazdy z elektrycznie sterowanymi fotelami mogą zdefiniować grupę ustawień dla fotela kierowcy i inną dla fotela pasażera z przodu, przy czym obie grupy zawierają ustawienia o identycznych nazwach.

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

Ustawienia

Ustawienie, komunikat (klasa Setting), reprezentuje pojedyncze ustawienie w SettingsGroup. Ten komunikat składa się z klucza, czyli nazwy ustawienia, oraz wartości, która może być jednego z kilku typów.

Ten przykład pokazuje, jak ustawienie jest reprezentowane za pomocą klucza i wartości:

// 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;
  }
}

Definicje ustawień

Definicje ustawień (SettingDefinition class) służą jako szablony poszczególnych ustawień w grupie ustawień. Określają one podstawowe cechy ustawienia, np. czy jest ono udostępniane wszystkim użytkownikom, czy jest specyficzne dla każdego użytkownika, czy też jest zarządzane zewnętrznie (przekazywane).

Definicje ustawień określają też wszelkie ograniczenia dotyczące wartości ustawienia, takie jak wartości minimalne i maksymalne, dopuszczalne przyrosty lub ograniczony zestaw opcji. Te ograniczenia zapewniają integralność i spójność danych w przypadku każdego ustawienia.

// 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;
}

Przykładowa sekwencja zdarzeń, gdy użytkownik aktualizuje ustawienie

Ten przykład ilustruje kolejność zdarzeń, które występują, gdy użytkownik używa systemu operacyjnego Android Automotive (AAOS) w samochodowym systemie multimedialnym (IVI) do aktualizowania ustawienia:

Wydarzenia, gdy użytkownik zmieni ustawienie

Rysunek 1. Wydarzenia, gdy użytkownik zmieni ustawienie.

Przykładowa sekwencja zdarzeń podczas uruchamiania pojazdu

Ten przykład pokazuje, jak preferencje użytkownika SDV stosują preferowane ustawienia użytkownika podczas uruchamiania pojazdu:

Zdarzenia podczas uruchamiania pojazdu

Rysunek 2. Zdarzenia, gdy pojazd jest uruchamiany.