Proprietà HAL dell'utente

Molte architetture dei veicoli attuali contengono più unità di controllo elettronico (ECU) al di fuori del sistema di infotainment che controllano l'ergonomia, ad esempio le impostazioni dei sedili e la regolazione degli specchietti. In base alle attuali architetture hardware e di alimentazione, molte ECU si accendono prima del sistema di infotainment basato su Android. Queste ECU possono interfacciarsi con un sistema di infotainment basato su Android tramite il Vehicle Hardware Abstraction Layer (VHAL).

A partire da Android 11, il sistema operativo Android Automotive (AAOS) ha introdotto un nuovo insieme di proprietà nel VHAL per creare, cambiare, rimuovere e associare accessori esterni per identificare gli utenti. Ad esempio, queste nuove proprietà consentono a un conducente di accoppiare un accessorio esterno, come un telecomando, al proprio utente Android. Quando il conducente si avvicina al veicolo, un'ECU si riattiva e rileva il telecomando. Questa ECU indica all'HAL quale utente Android deve avviare il infotainment, il che riduce il tempo di attesa del conducente per il caricamento dell'utente Android.

Attivare l'HAL utente

Le proprietà HAL utente devono essere attivate esplicitamente assicurandosi che la proprietà android.car.user_hal_enabled del sistema sia impostata su true. Puoi eseguire questa operazione nel file car.mk, in modo che non sia necessario impostarla manualmente. Verifica che user_hal_enabled=true sia attivato dumpando UserHalService:

$ adb shell dumpsys car_service --hal UserHalService|grep enabled
user_hal_enabled=true

Puoi anche controllare user_hal_enabled utilizzando adb shell getprop android.car.user_hal_enabled o adb logcat CarServiceHelper *:s. Se la proprietà è disattivata, viene visualizzato un messaggio simile al seguente quando viene avviato system_server:

I CarServiceHelper: Not using User HAL

Per attivare manualmente user_hal_enabled, imposta la proprietà di sistema android.car.user_hal_enabled e riavvia system_server:

$ adb shell setprop android.car.user_hal_enabled true
$ adb shell stop && adb shell start

L'output logcat viene visualizzato come segue:

I CarServiceHelper: User HAL enabled with timeout of 5000ms
D CarServiceHelper: Got result from HAL: OK
I CarServiceHelper: User HAL returned DEFAULT behavior

Proprietà HAL utente

Proprietà del ciclo di vita dell'utente

Le seguenti proprietà forniscono le informazioni HAL per gli stati del ciclo di vita dell'utente, che consentono la sincronizzazione del ciclo di vita dell'utente tra il sistema Android e un'ECU esterna. Queste proprietà utilizzano un protocollo di richiesta e risposta, in cui il sistema Android effettua una richiesta impostando un valore della proprietà e l'HAL risponde emettendo un evento di modifica della proprietà.

Nota: se l'HAL utente è supportato, è necessario implementare tutte le seguenti proprietà.

proprietà HAL Descrizione
INITIAL_USER_INFO
(lettura/scrittura)
Questa proprietà viene chiamata dal sistema Android per determinare quale utente Android viene avviato dal sistema all'avvio del dispositivo o al riavvio da Sospensione in RAM (STR). Quando viene chiamato, l'HAL deve rispondere con una di queste opzioni:
  • Il comportamento predefinito impostato da Android (passaggio all'ultimo utente utilizzato o creazione di un nuovo utente se si tratta del primo avvio).
  • Passa a un utente esistente.
  • Crea un nuovo utente (con le proprietà facoltative di nome, flag, impostazioni internazionali del sistema e così via) e passa a questo nuovo utente.

Nota: se l'HAL non risponde, il comportamento predefinito è eseguire l'operazione dopo un periodo di timeout (cinque secondi per impostazione predefinita), il che ritarda l'avvio. Se l'HAL risponde, ma il sistema Android non riesce a eseguire l'azione (ad esempio se è stato raggiunto il numero massimo di utenti), viene utilizzato il comportamento predefinito.

Esempio: per impostazione predefinita, il sistema Android si avvia nell'ultimo utente attivo all'avvio. Se viene rilevato un telecomando per un altro utente, l'ECU sostituisce la proprietà HAL e, durante l'avvio, il sistema Android passa all'avvio nell'utente specificato.

SWITCH_USER
(lettura/scrittura)
Questa proprietà viene chiamata quando si cambia l'utente Android attivo in primo piano. La proprietà può essere chiamata dal sistema Android o dall'HAL per richiedere il passaggio di un utente. I tre flussi di lavoro sono:
  • Moderno. Il passaggio è iniziato il giorno CarUserManager.
  • Legacy. Passaggio avviato da ActivityManager.
  • Veicolo. Chiamato dall'HAL per richiedere il passaggio a un altro utente.

Il flusso di lavoro moderno utilizza un approccio di commit in due fasi per garantire la sincronizzazione del sistema Android e dell'ECU esterna. Quando Android avvia il trasferimento:

  1. Controlla l'HAL per determinare se l'utente può essere trasferito.

    L'HAL risponde con SUCCESS o FAILURE, in modo che Android sappia se procedere o meno.

  2. Completa il trasferimento dell'utente ad Android.

    Android invia una risposta ANDROID_POST_SWITCH all'HAL per indicare l'esito positivo o negativo del trasferimento.

L'HAL deve attendere la risposta ANDROID_POST_SWITCH per aggiornare il proprio stato al fine di sincronizzare le ECU o aggiornare altre proprietà HAL.

Esempio: durante la guida, un conducente tenta di cambiare utente Android nell'interfaccia utente dell'infotainment. Tuttavia, poiché le impostazioni del sedile dell'auto sono legate all'utente Android, il sedile si sposta durante il cambio dell'utente. Di conseguenza, la ECU che controlla i posti non conferma il cambio, l'HAL risponde con un errore e l'utente Android non viene commutato.

Il flusso di lavoro Legacy è una chiamata unidirezionale inviata dopo il trasferimento dell'utente (quindi l'HAL non può bloccare il trasferimento). Viene chiamato solo all'avvio (dopo il cambio iniziale dell'utente) o per le app che chiamano ActivityManager.switchUser() anziché CarUserManager.switchUser(). Le app di riferimento Settings e SystemUI utilizzano già quest'ultima, ma se un OEM fornisce le proprie app Impostazioni per cambiare utente, gli OEM devono modificare l'utilizzo.

Esempio: se un'app utilizza ActivityManager.switchUser() per cambiare utente, viene inviata una chiamata unidirezionale all'HAL per informare che è stato eseguito un cambio di utente.

Il flusso di lavoro del veicolo ha origine dall'HAL, non dal sistema Android:

  1. L'HAL richiede il passaggio da un utente all'altro.
  2. Il sistema completa il trasferimento dell'utente Android.
  3. Android invia una risposta ANDROID_POST_SWITCH all'HAL per indicare il successo o l'errore del passaggio.

Esempio: Bob ha usato il telecomando di Alice per aprire l'auto e l'HAL ha risposto alla richiesta INITIAL_USER_INFO con l'ID utente di Alice. Successivamente, un'ECU del sensore biometrico ha identificato il conducente come Bob, quindi l'HAL utente ha inviato una richiesta SWITCH_USER per cambiare utente.

CREATE_USER
(lettura/scrittura)
Questa proprietà viene chiamata dal sistema Android quando viene creato un nuovo utente Android (utilizzando l'API CarUserManager.createUser()).

L'HAL risponde con SUCCESS o FAILURE. Se l'HAL risponde con un errore, il sistema Android rimuove l'utente.

Esempio: un conducente tocca un'icona dell'interfaccia utente dell'infotainment per creare un nuovo utente Android. Viene inviata una richiesta all'HAL e al resto dei sottosistemi del veicolo. Le ECU vengono informate dell'utente appena creato. Altri sottosistemi e ECU associano il proprio ID utente interno all'ID utente Android.

REMOVE_USER
(solo SCRITTURA)
Il sistema Android chiama questa proprietà dopo la rimozione di un utente Android (con il metodo CarUserManager.removeUser()).

Si tratta di una chiamata unidirezionale, non è prevista alcuna risposta dall'HAL.

Esempio: un conducente tocca per rimuovere un utente Android esistente nell'interfaccia utente del sistema di infotainment. L'HAL viene informato e gli altri sottosistemi e le ECU del veicolo vengono informati della rimozione dell'utente in modo da poter rimuovere il proprio ID utente interno.

Proprietà aggiuntive

Di seguito sono riportate proprietà aggiuntive non correlate agli stati del ciclo di vita dell'utente. Ciascuno può essere implementato senza supportare l'HAL utente.

proprietà HAL Descrizione
USER_IDENTIFICATION_ASSOCIATION
(lettura/scrittura)
Utilizza questa proprietà per associare qualsiasi utente Android a un meccanismo di identificazione, ad esempio un telecomando o uno smartphone. Utilizza la stessa proprietà per le associazioni get o set.

Esempio: un conducente tocca un'icona dell'interfaccia utente di infotainment per associare il telecomando usato per aprire il veicolo (KEY_123) all'utente Android attivo corrente (USER_11).

Librerie helper

Tutti gli oggetti utilizzati nei messaggi di richiesta e risposta (ad esempio UserInfo, InitialUserInfoRequest, InitialUSerInfoResponse e così via) hanno una rappresentazione di alto livello che utilizza struct C++, ma la rimozione deve essere appiattata in oggetti struct standard (vedi gli esempi di seguito).VehiclePropValue Per facilità di sviluppo, in AOSP viene fornita una libreria helper C++ per convertire automaticamente l'HAL dell'utente structs in un VehiclePropValue (e viceversa).

Esempi

INITIAL_USER_INFO

Esempio di richiesta (al primo avvio)

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
}

Esempio di risposta (crea utente amministratore)

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

Il nome effettivo delle classi e delle proprietà è leggermente diverso, ma il flusso di lavoro complessivo è lo stesso, come illustrato nella figura:

Flusso di lavoro

Figura 1. Flusso di lavoro delle proprietà HAL utente.

Esempio di richiesta di flusso di lavoro moderno

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

Esempio di risposta moderna per un flusso di lavoro

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
}

Esempio di risposta post-cambio di un flusso di lavoro moderno

Questa risposta si verifica in genere quando il trasferimento da Android ha esito positivo:

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

Risposta del flusso di lavoro moderno dopo il passaggio

Questa risposta si verifica in genere quando si verifica un errore di trasferimento su 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)
}

Esempio di richiesta di flusso di lavoro precedente

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

Esempio di richiesta del flusso di lavoro del veicolo

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
}

Risposta al flusso di lavoro precedente dopo il passaggio

Questa risposta si verifica in genere quando il trasferimento da Android ha esito positivo:

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

Esempio di richiesta

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

Esempio di risposta

VehiclePropValue { // flattened from CreateUserResponse
prop: 299896585 // CREATE_USER
prop.values.int32Values:
 [0] = 42        // Request ID (must match request)
 [1] = 3         // CreateUserStatus::SUCCESS
}

REMOVE_USER

Esempio di richiesta

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

Esempio di set (telecomando associato all'utente 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
}